maksim 发布的文章

■ SQL注入

场景:SQL注入发生在任何有用户可控输入并且会和数据库进行交互操作的功能处,比如:用户登录验证、查询、搜索、更新信息等。

说明:通过预编译实现参数化查询,将用户的输入数据和后台的SQL代码分离,正确的使用预编译来实现参数化查询,可以有效地防止SQL注入。

PHP 示例:使用PDO处理用户输入

<?php
    $host='localhost';
    $dbName='xxxxx';
    $user='xxxxxxx';
    $pass='xxxxxxx';
    $dsn="mysql:host=$host; dbname=$dbName";
    
    try {
        $pdo = new PDO($dsn, $user, $pass);
        if (version_compare(PHP_VERSION, '5.3.6', '<') && !defined('PDO::MYSQL_ATTR_INIT_COMMAND')) {
            $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        }
    } catch (PDOException $e) {
        echo $e->getMessage();
        exit();
    }

    $pdo->exec("SET NAMES 'utf8'");
    $smt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    $smt->execute(array(':name' => $_GET['name']));
    // $smt->bindParam(':name', $_GET['name']); 
    foreach ($smt->fetchAll() as $row) {
        // do something with $row
    }
?>      

PHP 示例:使用Mysqli处理用户输入

<?php
    $host='localhost';
    $dbName='xxxxx';
    $user='xxxxxxx';
    $pass='xxxxxxx';
    
    try {
        $con = mysqli_connect($host, $user, $pass, $dbName)
        if (!&con) {
            echo mysqli_connect_error();
            exit();
        }
    } catch (PDOException $e) {
        echo $e->getMessage();
        exit();
    }
    $con->set_charset("utf8");
    $smt = $con->prepare("SELECT * FROM employees WHERE name = ?");
    $smt->bindParam("s", $_GET['name']);
    $smt->execute();
    $ret = $smt->get_result();
    while ($row = $ret->fetch_assoc()) {
        // do something with $row
    }
    mysqli_close($con);
?>  

■ XSS注入

场景:所有存在用户可控输入,而且会将用户输入输出到客户端的场景,比如:用户论坛发帖、用户评论、用户昵称、用户个人简介、用户提交反馈管理员后台查看、课程命名、课程信息介绍等等。

说明:对输入的数据进行过滤和转义,包含但不限于<>”’%()&+\’\”等危险特殊字符。

PHP示例:先对用户输入的内容输出进行html实体转码。

public function SafeFilter($arr){
    $ra=Array('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/','/script/','/javascript/','/vbscript/','/expression/','/applet/','/meta/','/xml/','/blink/','/link/','/style/','/embed/','/object/','/frame/','/layer/','/title/','/bgsound/','/base/','/onload/','/onunload/','/onchange/','/onsubmit/','/onreset/','/onselect/','/onblur/','/onfocus/','/onabort/','/onkeydown/','/onkeypress/','/onkeyup/','/onclick/','/ondblclick/','/onmousedown/','/onmousemove/','/onmouseout/','/onmouseover/','/onmouseup/','/onunload/');
    if (is_array($arr)){
        foreach ($arr as $key => $value){
            if (!is_array($value)){
                //不对magic_quotes_gpc转义过的字符使用addslashes(),避免双重转义。
                if (!get_magic_quotes_gpc()){
                    $value = addslashes($value);  //给单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)加上反斜线转义
                }
                $value = preg_replace($ra,'',$value); //删除非打印字符,粗暴式过滤xss可疑字符串

                $arr[$key] = htmlentities(strip_tags($value)); //去除 HTML 和 PHP 标记并转换为 HTML 实体
            }else{
                SafeFilter($arr[$key]);
            }
        }
    }
}       

■ XML注入

场景:XML被设计用来传输和存储数据,所以XML注入发生在使用XML传输或存储数据,而对用户输入没有严格验证处理的场景。

说明:在XML文档内部或外部引用数据时,过滤用户提交的参数,如<、>、’、“、&等特殊字符。禁止加载外部实体,禁止报错。

PHP示例:假设一个在线商城可以由用户指定商品数量,来完成支付,会生成如下的XML:

<?php
    if (isset($_SERVER['HTTP_CLIENT_IP']) || isset($_SERVER['HTTP_X_FORWARDED_FOR']) || 
       !in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1',))) {
        header('HTTP/1.0 403 Forbidden');
        exit('You are not allowed to access this file.');
    }
?>          

title = ""
categories = "PHP"
date =

+++

■ 控制方法

场景:需要进行授权访问的系统在进行访问时接入。

说明:将访问控制的逻辑代码与应用程序其他代码分开,服务端根据会话标识来进行访问控制管理。

PHP示例:根据用户组对用户权限进行独立的访问控制。

<?php
    public function getPermission() {
        $this->is_login();
        $user_id = session('id');
        return $user_privileges = DB::table('userPermission')
                                  ->where('user_id', $user_id)
                                  ->get()
                                  ->toArray();
    }
?>

■ 控制管理

场景:用户访问权限校验。

说明:限制只有授权的用户才能访问受保护的URL、文件、服务、应用数据、配置、直接对象引用等。

PHP示例:用户访问URL时对其权限进行校验。

<?php
    namespace App\Http\Controllers\Data;
    use App\Http\Controllers\BaseController;
    
    public function preHandle() {
        $privileges = $this->getPermission();
        if (!$privileges) {
            return $this->responseError('没有访问权限');
        }
        // Handle logic here
    }
?>

■ 接口管理

场景:在那些具有安全性要求的应用程序中,需要检查应用程序是否能访问一些有限的资源,例如文件、套接字(socket) 等。

说明:限制只有授权的用户才能访问受保护的URL、文件、服务、应用数据、配置、直接对象引用等。

(coding...)

■ 权限变更

场景:权限发生变化时。

说明:当权限发生变更时,应记录日志,并通知用户是否是本人操作,告知存在的安全风险。

PHP示例:权限变更时通知。

<?php
    date_default_timezone_set('PRC');
     
    public function writeLog($filename, $msg) {
        $file = dirname(__FILE__) . '/logs/' . $filename . date('Ym') . '.log';
        !is_dir(dirname($file)) && mkdir(dirname($file), 0777, true);
        $handle = fopen($file, 'a');
        flock($handle, LOCK_EX);
        fwrite($handle, sprintf("%s %s\r\n", date('Y-m-d H:i:s',time()), $msg));
        flock($handle, LOCK_UN);
        fclose($handle);
    }
    
    public function roleUpdate() {
        require "DBDA.class.php";
        $db = new DBDA();
        $uid = $_POST["uid"];
        $js = $_POST["js"];
        
        $privileges = $this->getPermission();
        if (!$privileges) {
            return $this->responseError('没有操作权限');
        }
        
        $con = mysqli_connect($host, $user, $pass, $dbName)
        $con->set_charset("utf8");
        $smt = $con->prepare("DELETE FROM userRole WHERE userid=?");
        $smt->bindParam("s", $uid);
        $smt->execute();
        writeLog("admin", "DELETE FROM userRole WHERE userid=".$uid);
         
        $ajs = explode(",", $js);
        foreach($ajs as $v){
            $smt = $con->prepare("INSERT INTO userinjuese SET uid=?, value=?");
            $smt->bindParam("ss", $uid, $v);
            $smt->execute();
            writeLog("admin", "INSERT INTO userinjuese SET uid=".$uid.", value=".$v);
        }
    }
?>

■ 验证码生成

场景:用户注册、密码重置、密码找回时。

说明:复杂度至少4位数字或字母,或者采用拼图等验证方式,一次一用,建议有效期不超过180秒。 Java示例:后端生成验证码。

PHP 示例:后端生成验证码。

<?php
session_start();
$width = 100;
$height = 30;
$image = imagecreatetruecolor($width, $height);
$black = imagecolorallocate($image, 0, 0, 0);
$bgcolor = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $bgcolor);
 
$captch_code = "";
for ($i=0; $i<4; $i++) {
    $fontsize = 6;
    $fontcolor = imagecolorallocate($image, rand(0, 100), rand(0, 100), rand(0, 100));
    $data = 'abcdefghijkmnpqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
    $fontcontent = substr($data, rand(0, strlen($data)-1), 1);
    $captch_code .= $fontcontent;
    $x = ($i*100/4) + rand(5, 10);
    $y = rand(5, 15);
    imagestring($image, $fontsize, $x, $y, $fontcontent, $fontcolor);
}
$_SESSION['captch_code'] = $captch_code;
 
for ($i<0; $i<200; $i++) {
    $pointcolor = imagecolorallocate($image, rand(50, 200), rand(50, 200), rand(50, 200));
    imagesetpixel($image, rand(1, $width-1), rand(1, $height-1), $pointcolor);
}
 
for ($i=0; $i<3; $i++) {
    $linecolor = imagecolorallocate($image, rand(100, 200), rand(100, 200), rand(100, 200));
    imageline($image, rand(1, $width-1), rand(1, $height-1), rand(1, $width-1), rand(1, $height-1), $linecolor);
}
 
header('Content-Type:image/png');
imagepng($image);
imagedestroy($image);
?>

html 用户登录页面增加验证码功能

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
    <form method="post">
        <table>
            <tr><td>用户名:</td><td><input type="text" name="username" placeholer="请输入用户名……"/></td></tr>
            <tr><td>密  码:</td><td><input type="password" name="password" /></td></tr>
            <tr><td>验证码:</td><td><input type='text' name='check' id='icode' value=''/><span id='checkcode'></span></td></tr>
            <tr><td><img id='captch' border='1' src="__CONTROLLER__/image?r=<?php echo rand();?>" width='100' heigth='30'/></td>
            <td><a href='javascript:void(0)' onclick="document.getElementById('captch').src='__CONTROLLER__/image?r='+Math.random()">看不清?</a></td></tr>
            <tr><td><input type="reset" value="取消" /></td><td><input type="submit" value="登录" /></td></tr>
        </table>
    </form>
    <script>
    document.getElementById("icode").onblur=function() {
        if (window.XMLHttpRequest) {
            request = new XMLHttpRequest();
        } else {
            request = new ActiveXObject("Microsoft.XMLHTTP");
        }
        var url="check?checkcode="+document.getElementById("icode").value;
        request.open('GET', url, true);
        request.send();
        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                if (request.status == 200) { 
                    document.getElementById("checkcode").innerHTML = request.responseText;
                } else {
                    alert("错误:" + request.status);
                }
            } 
        }        
    }
    </script>
</body>
</html>

验证码校验功能实现。

<?php
    function checkCode($code, $userid) {
        session_start();
        if (!isset($_SESSION[static::MESSAGE_CODE_TIME]) || !isset($_SESSION[static::MESSAGE_CODE])) {
            echo "Error: code is null.";
            return -1;
        }
        if ($_SESSION[static::MESSAGE_CODE_TIME] + 60*3 < time()) {
            $this->unsetCode();
            echo "Error: code timeout.";
            return -1;
        }
        if ($_SESSION[static::MESSAGE_CODE] == $code && $_SESSION[static::MESSAGE_CODE_USER] == $userid) {
            $this->unsetCode();
            return 0;
        }
        echo "Error, check code fail.";
        return -1;
    }
?>

■ 验证码生成/验证码限制/安全提示/凭证校验

场景:用户注册、密码重置、密码找回时。

说明:复杂度至少6位数字或字母,一次一用,建议有效期不超过180秒。前后端设置用户获取频率为60秒一次,建议每个用户每天获取的短信最多10条。增加安全提示:至少含本次操作的功能、验证码发送编号、是否是个人自己操作的风险等信息。禁止在响应中返回验证码,服务器端同时校验密码、短信验证码等凭证信息,防止出现多阶段认证绕过的漏洞。

<?php
    function sendCode($phone) {
        session_start();
        $time = time();
        if ($_SESSION[static::MESSAGE_CODE_USER] && $_SESSION[static::MESSAGE_CODE_TIME] + 60 > $time) {
            echo "Error: send sms code too frequeue, try it later.";
            return -1;
        }
        if (!isset($_SESSION[static::SESSION_MESSAGE_CODE_COUNT])) {
            $_SESSION[static::SESSION_MESSAGE_CODE_COUNT] = 1;
        } else {
            $_SESSION[static::SESSION_MESSAGE_CODE_COUNT]++;
        }
        if ($_SESSION[static::SESSION_MESSAGE_CODE_COUNT] >= 5) {
            $this->unsetCode();
            echo "sms code send time more than five, try it later...";
            return -1;
        }
        setcookie(session_name(), session_id(), $time+60,"/");
        $phone = $_GET["phone"];
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "http://apis.xxx.com/sms/send?mobile={$phone}&key={$key}");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $json = curl_exec($ch);
        $rets = json_decode($json, true);
        $_SESSION[static::MESSAGE_CODE_USER] = $phone;
        $_SESSION[static::MESSAGE_CODE] = $rets['smscode'];
        $_SESSION[static::MESSAGE_CODE_TIME] = $time;
        echo $rets['error_code'];
        return 0;
    }

    function checkCode($code, $phone) {
        session_start();
        if (!isset($_SESSION[static::MESSAGE_CODE_TIME]) || !isset($_SESSION[static::MESSAGE_CODE])) {
            echo "Error: sms code is null.";
            return -1;
        }
        if ($_SESSION[static::MESSAGE_CODE_TIME] + 60*3 < time()) {
            $this->unsetCode();
            echo "Error: sms code is timeout.";
            return -1;
        }
        if ($_SESSION[static::MESSAGE_CODE] == $code && $_SESSION[static::MESSAGE_CODE_USER] == $phone) {
            $this->unsetCode();
            echo "OK, check sms code success.";
            return 0;
        }
        echo "Error, check sms code fail.";
        return -1;
    }
?> 

提交凭证

场景:用户通过PC或者APP进行登录时,需要保护用户凭证避免被泄露。

说明:用户凭证必须经过加密且以POST方式提交,建议用HTTPS协议来加密通道、认证服务器。

HTML示例:用户登录功能。

<html>
    <form action="LoginServlet" method="post" onSubmit="return validate(this)">  
        用户名:<input type="text" name="name">&l;tbr>
        密  码:<input type="password" name="password"><br>  
        <input type="submit" value="登录">  
        <input type="reset" value="重置">
    </form>  
</html>
          
        

■ 错误提示+异常处理

场景:用户通过PC或者APP进行登录时,登录失败提示信息避免泄露过多信息。同时能够防止撞库等发生。

说明:安全地处理失败的身份验证,如使用“用户名或密码错误”来提示失败,防止泄露过多信息。登录入口应具有防止暴力或撞库猜解(利用已泄露的密码字典进行批量登录尝试)的措施,超过1次验证失败自动启用图灵测试,超过多次验证失败自动启用账户锁定机制限制其访问。

Java示例:用户登录失败提示,异常登录锁定等。

public class Login extends HttpServlet {  
    private static final long serialVersionUID = 1L;
    public boolean localdateLtDate(String date) throws Exception {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
            Date dates = sdf.parse(date);
            Date now = sdf.parse(sdf.format(new Date()));
            if (now.getTime() - dates.getTime() > 24*60*60*1000) {
                return true;
            } else {
                return false;
            }
    }  
    @Override    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
        request.setCharacterEncoding("utf-8");  
        String username = request.getParameter("username").trim();  
        String password = request.getParameter("password").trim();  
        if (username==null || "".equals(username)) { 
            response.sendRedirect("login.html"); 
        }
        if (password==null || "".equals(password)) {
            response.sendRedirect("login.html");   
        }

        String lockFlag = "";
        String failureNum = "";
        String loginDate = "";
        String nowDate = "";
        if (service.checkLoginRecord(userName)) {
            ResultSet rs = service.getLatestLoginRecord(userName);
            if (rs != null && rs.next()) {
                    lockFlag = rs.getString("LOCK_FLAG");
                    failureNum = rs.getString("FAILURE_NUM");
                    loginDate = rs.getString("LOGIN_DATE");
            }
            if ("1".equals(lockFlag)) {
                    // lock time long than 1 day to unlock.
                    if (service.localdateLtDate(loginDate)) {
                        service.deleteLoginRecord(userName);
                        lockFlag = "";
                        failureNum = "";
                    loginDate = "";
                    }
            }
        }
        User user = new User();  
        user.setName(username); 
        user.setPassword(password);  
        List<String> info = new ArrayList<String>();  
        UserPassPort userPassPort = new UserPassPort();  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");   
        String nowDate = sdf.format(new Date());
        try {               
            if(UserPassPort.findValidUser(user)){  
                service.insertLoginRecord(userName, "0", "0", nowDate); 
                request.getSession().setAttribute("LoginFlag", "1");  
                info.add("登录成功!"); 
                request.setAttribute("info", info);  
                request.getSession().setMaxInactiveInterval(30*60);
                request.getRequestDispatcher("index.jsp").forward(request, response); 
            } else { 
                if ("".equals(failureNum)) {
                    service.insertLoginRecord(userName, "0", "1", nowDate);
                    info.add("用户名或密码错误!");  
                        } else {
                                if ("1".equals(lockFlag)) {
                                    response.sendRedirect("login.html");  
                        info.add("用户名或密码错误!");
                        request.setAttribute("info", info);  
                        response.sendRedirect("login.html");   
                                }
                                failt = Integer.parseInt(failureNum);
                                if (failt < 5) {
                                service.updateLoginRecord(userName, "0", String.valueOf((failt+1)), nowDate);
                        info.add("用户名或密码错误!");
                        request.setAttribute("info", info);  
                                response.sendRedirect("login.html");   
                        } else {
                            service.insertLoginRecord(userName, "1", "1", nowDate);
                        info.add("超过最大登录失败次数,已被锁定!");
                        request.setAttribute("info", info);  
                        response.sendRedirect("login.html");   
                        }
                    }
            }                         
        } catch (Exception e) {  
            response.sendRedirect("login.html");   
        }   
        }  
    @Override    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
    }  
}  
          
        

二次验证

场景:重要操作需要二次校验防止跨站请求伪造攻击。

说明:在执行关键操作(如账户密码修改、资料更新、交易支付等)时,先启动图灵测试,再对用户身份进行二次验证。交易支付过程还应该形成完整的证据链,待交易数据应经过发起方数字签名。

Java示例:验证码的生成和校验实现。

A.验证码生成和确认:

public class drawCode extends HttpServlet {
    public drawCode() {
        super();
    }
    public void destroy() {
        super.destroy(); 
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("Pragma", "No-cache");    
        response.setHeader("Cache-Control", "no-cache"); 
        response.setDateHeader("Expires", 0);     
        int width = 60, height = 20;
        BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        g.setColor(new Color(200, 200, 200));
        g.fillRect(0, 0, width, height);
        Random rnd = new Random();
        int randNum = rnd.nextInt(8999) + 1000;
        String randStr = String.valueOf(randNum);
        request.getSession().setAttribute("randStr", randStr);
        g.setColor(Color.black);
        g.setFont(new Font("", Font.PLAIN, 20));
        g.drawString(randStr, 10, 17);
        for (int i = 0; i < 100; i++){
            int x = rnd.nextInt(width);
            int y = rnd.nextInt(height);
            g.drawOval(x, y, 1, 1);
        }  
        ImageIO.write(image, "JPEG", response.getOutputStream());
    }
    public void init() throws ServletException {
        // Put your code here
    }
}
          
        

验证码校验ValidateServlet.java文件:

public class ValidateServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");      
        String code = request.getParameter("code");
        String randStr = request.getSession().getAttribute("randStr").toString();
        System.out.println(randStr);
        if (!code.equals(randStr)) {
            response.sendRedirect("/example/login.jsp?codeErro=yes");
            return ;
        } else {
            response.sendRedirect("/example/login.jsp?codeErro=no");
            return ;
        }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);      
    }
}
          
        

B.登录页面JSP文件:

<%@ page language="java" pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 
<html>
    <body>
        <script type="text/javascript">     
            function refresh() {                
                url = "/example/servlet/drawCode?" + parseInt(100*Math.random());  
                document.getElementById("imgValidate").src = url;
            }   
        </script>
        <br>
        <form name="loginForm" action="/example/servlet/ValidateServlet">
            旧密码:<input type="password" name="oldpasswd" style="height:25px"/><br/><br/>
            新密码:<input type="password" name="newpasswd" style="height:25px" /><br/><br/>
            确认新密码:<input type="password" name="confirmpasswd" style="height:25px" /><br/><br/>
            验证码:
            <input type="text" name="code" size="10" style="height:25px;vertical-align:middle">
            <img id="imgValidate" src="/example/servlet/drawCode" style="vertical-align:middle">
            <img src="/example/image/refresh.jpg" width="25" onclick="refresh()" style="vertical-align:middle"><br/><br/>
            <input type="submit" value="确定修改">
        </form>
        <c:set var="codeErro" value="${param.codeErro}"/>
        <c:if test="${codeErro=='yes'}"><p><span style="color:red;">验证码错误</span></p></c:if>
         <c:if test="${codeErro=='no'}"><p><span style="color:green;">验证码正确</span></p></c:if>
    </body>
</html>
          
        

备注:http://cighao.com/2015/08/21/validateCode-of-java/

PHP示例:验证码的生成和校验实现。

<?php 
header('Content-Type: text/html; charset=utf-8');
session_start();
require dirname(__FILE__).'/includes/global.func.php';

if ($_GET['action'] == 'verification') {
    if (!($_POST['code'] == $_SESSION['code'])) {
        _alert_back('验证码不正确!');
    }else{
        _alert_back('验证码通过!');
    }
}  
?>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>verification code</title>
    <link rel="stylesheet" type="text/css" href="style/basic.css" />
    <script type="text/javascript" src="js/codeimg.js"></script>
</head>
<body>
    <div id="testcode">
        <form method="post" name="verification" action="verification-code.php?action=verification">
            验证码:<input type="text" name="code" class="code" />
                    <img src="codeimg.php" id="codeimg"  />
                    <input type="submit" class="submit" value="验证" />
               </form>
        </div>
</body>
</html>
          
        

■ 多因子认证

场景:在类似提交交易订单等环节需要防止跨站请求伪造攻击。

说明:高度敏感或核心的业务系统,建议使用多因子身份验证机制,如短信验证码、软硬件Token等。

Java示例:以使用软件Token为例进行说明服务器和客户端交互实现。

A.首先在服务器端Servlet中添加如下代码:

package com.example.util;  
import java.util.ArrayList;  
import javax.servlet.http.HttpSession;  

public class Token {  
    private static final String TOKEN_LIST = "tokenList";  
    public static final String TOKEN_STRING = "token";  
    private static ArrayList getTokenList(HttpSession session) {  
        Object obj = session.getAttribute(TOKEN_LIST);  
        if (obj != null) {  
            return (ArrayList) obj;  
        } else {  
            ArrayList tokenList = new ArrayList();  
            session.setAttribute(TOKEN_LIST, tokenList);  
            return tokenList;
        }
    }  
    public static String getTokenString(HttpSession session) {  
        String tokenStr = (new Long(System.currentTimeMillis()).toString());
        ArrayList tokenList = getTokenList(session);  
        tokenList.add(tokenStr);  
        session.setAttribute(TOKEN_LIST, tokenList);       
        return tokenStr;  
    }
    public static boolean isTokenStringValid(String tokenStr, HttpSession session) {  
        boolean valid = false;  
        if (session != null) {
            ArrayList tokenList = getTokenList(session);  
            if (tokenList.contains(tokenStr)) {  
                valid = true;  
                tokenList.remove(tokenStr);  
            }  
        }  
        return valid;  
    }  
}

if (Token.isTokenStringValid(request.getParameter(Token.TOKEN_STRING_NAME), request.getSession())) {  
//To Do Here.
}
          
        

B. 然后在JSP页面增加以下代码:

<%@ page import="com.example.util.Token" %>  
<form>
    <input type="hidden" name="<%=Token.TOKEN_STRING %>" value="<%=Token.getTokenString(session) %>">  
</form>
          
        

PHP示例:以使用软件Token为例进行说明服务器和客户端交互实现。

<?php
session_start();
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = base64_encode(openssl_random_pseudo_bytes(32));
}

if (isset($_POST['csrf_token']) && $_POST['csrf_token'] === $_SESSION['csrf_token']) {
    exit("POST data is valid.");
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" /> 
    <title>PHP CSRF Protection</title>

    <script>
    window.csrf = { csrf_token: '<?php echo $_SESSION['csrf_token']; ?>' };

    $.ajaxSetup({
        data: window.csrf
    });

    $(document).ready(function() {
        $.post('/awesome/ajax/url', { foo: 'bar' }, function(data) {
            console.log(data);
        });
    });
    </script>
</head>
<body>
    <form action="index.php" method="post" accept-charset="utf-8">
        <input type="text" name="foo" />
        <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>" />
        <input type="submit" value="Submit" />
    </form>
</body>
</html>