如何保护 AJAX 或 javascript Web 应用程序

Posted

技术标签:

【中文标题】如何保护 AJAX 或 javascript Web 应用程序【英文标题】:How to protect AJAX or javascript web application 【发布时间】:2020-07-03 07:56:58 【问题描述】:

这是一个简单的函数,它使用 AJAX 并在单击按钮时获取数据库中 id=219 的图像信息

任何加载此网页的人都可以通过转到源代码来更改 javascript 代码。 然后通过单击按钮,他将运行修改后的代码(例如将 image_id 从 219 更改为 300)。所以他可以通过改变 image_id 来获取任何图像的信息

问题是如何防范客户端攻击或 XSS?

function clicked () 
    var xhttp = new XMLHttpRequest () ;

    xhttp.onreadystatechange = function () 
        if (this.readyState == 4 && this.status == 200)
            var obj = JSON.parse (this.responseText);
            alert (obj.description);
        
    ;

    xhttp.open ("POST","get_title_description.php", true);
    xhttp.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
    xhttp.send ("image_id=219") ;

【问题讨论】:

黄金法则是.. 永远不要相信客户。在服务器端进行安全/验证/身份验证 前端和后端都必须进行验证,因为有人可能会使用邮递员之类的东西绕过前端验证,因此服务器必须验证它收到的内容。这是必须的 我使用 PHP 作为后端语言。你能发布一个关于如何在后端防止这种情况的例子吗?也许一种方法是检查会话 id 是否等于该图像表中的 user_id ? (以确保他不能查看其他用户的图像) 您应该只允许在客户端提供 cookie 的情况下获取图像 - 并且此 cookie 必须具有 HttpOnly 标志并使用一种对称加密算法(例如 AES-256)进行加密。 cookie 的有效期必须很短 - 例如10-15 分钟 @IVOGELOV 该 cookie 的值应该是多少?并且基于什么应该允许用户获取图像? 【参考方案1】:

您可以使用类似的东西来生成和验证 cookie:

define('COOKIE_TOKEN', 'my_token');

class BaseAuth

  protected $uid;

  private static function base64url_encode(string $s): string
  
    return strtr($s,'+/=','-|_');
  

  private static function base64url_decode(string $s): string
  
    return strtr($s,'-|_','+/=');
  

  // Encodes after encryption to ensure encrypted characters are URL-safe
  protected function token_encode(String $string): string
  
    $iv_size = openssl_cipher_iv_length(TYPE_CRYPT);
    $iv = openssl_random_pseudo_bytes($iv_size);

    $encrypted_string = @openssl_encrypt($string, TYPE_CRYPT, SALT, 0, $iv);

    // Return initialization vector + encrypted string
    // We'll need the $iv when decoding.
    return self::base64url_encode($encrypted_string).'!'.self::base64url_encode(base64_encode($iv));
  

  // Decodes from URL-safe before decryption
  protected function token_decode(String $string): string
  
    // Extract the initialization vector from the encrypted string.
    list($encrypted_string, $iv) = explode('!', $string);
    $string = @openssl_decrypt(self::base64url_decode($encrypted_string), TYPE_CRYPT, SALT, 0, base64_decode(self::base64url_decode($iv)));
    return $string;
  

  // performs log-out
  public function clear_cookie()
  
    setcookie(COOKIE_TOKEN, '', time() - 300, '/api', '', FALSE, TRUE); // non-secure; HTTP-only
  

  private function userIP(): string
  
    return $_SERVER['REMOTE_ADDR'];
  

  // validates Login token
  public function authorized(): bool
  
    if(isset($_COOKIE[COOKIE_TOKEN]))
    
      $stamp = time();
      $text = $this->token_decode($_COOKIE[COOKIE_TOKEN]);
      if($text != '')
      
        $json = json_decode($text,TRUE);
        if(json_last_error() == JSON_ERROR_NONE)
        
          if($json['at'] <= $stamp AND $json['exp'] > $stamp AND $json['ip'] == $this->userIP() AND $json['id'] != 0)
          
            // check if user account is still active
            $res = $db->query("SELECT id,active,last_update,last_update > '".$json['last']."'::timestamptz AS expired FROM account WHERE id = ".$json['id']);
            $info = $db->fetch_assoc($res);
            if($info['active'] != 0)
            
              if($info['expired'] == 0)
              
                // extend the token lifetime
                $this->sendToken($info);
                $this->uid = $json['id'];
                return TRUE;
              
            
          
        
      
      $this->clear_cookie();
    
    return FALSE;
  

  public function login(String $username, String $password): bool
  
    $stm = $db-prepare("SELECT id,user_name AS username,user_pass,full_name,active,last_update,COALESCE(blocked_until,NOW()) > NOW() AS blocked 
      FROM account WHERE user_name = :user");
    $res = $stm->execute(array('user' => strtolower($json['username'])));
    if($res->rowCount())
    
      $info = $db->fetch_assoc($res);
      if($info['active'] == 0)
      
        // Account is disabled
        return FALSE;
      
      elseif($info['blocked'] != 0)
      
        // Blocked for 5 minutes - too many wrong passwords
        // extend the blocking
        $db->query("UPDATE account SET blocked_until = NOW() + INTERVAL 5 minute WHERE id = ".$info['id']);
        return FALSE;
      
      elseif(!password_verify($password, $info['user_pass']))
      
        // Wrong password OR username
        // block account
        $db->query("UPDATE account SET blocked_until = NOW() + INTERVAL 5 minute WHERE id = ".$info['id']);
        return FALSE;
      
      else
      
        unset($info['user_pass']);
        unset($info['blocked']);
        $this->sendToken($info);
        return TRUE;
      
    
  

如果您不需要对用户进行身份验证和授权,而只需要随机的不可预测的图像 ID - 您可以简单地使用 UUID。

【讨论】:

以上是关于如何保护 AJAX 或 javascript Web 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

如何保护仅从前端使用的 API(Ajax 调用)

从javascript传递到web api 2时如何隐藏或保护令牌

AJAX

如何从 Ajax 或 JavaScript 调用 Struts1 Action?

如何使用 Javascript 或 ajax 在文本字段中实时注入或添加输入字段的值?

Wordpress 使用 Wordpress 将 ajax 值传递到特定页面