zoukankan      html  css  js  c++  java
  • Web登陆防爆破的原理和实现

    0、写在前面

    本来只想做个验证码的功能实现,后来想想光要验证码也不行,干脆写整个防爆破的实现吧

    1、防护软件/硬件Waf/Web服务器限制单IP固定时间段的登陆频率

    1.1 作用

    通过WAF可以实现某一个IP访问频率过高时则将此IP加入黑名单一段时间

    通过Nginx等Web服务器可以实现限制单IP固定时间段的登陆频率,也就是限制流量

    可以防爆破的同时一定程度上防止DDos攻击

    1.2 实现

     1 http{
     2     ...
     3 
     4     #定义一个名为allips的limit_req_zone用来存储session,大小是10M内存,
     5     #以$binary_remote_addr 为key,限制平均每秒的请求为20个,
     6     #1M能存储16000个状态,rete的值必须为整数,
     7     #如果限制两秒钟一个请求,可以设置成30r/m
     8 
     9     limit_req_zone $binary_remote_addr zone=allips:10m rate=20r/s;
    10     ...
    11     server{
    12         ...
    13         location {
    14             ...
    15 
    16             #限制每ip每秒不超过20个请求,漏桶数burst为5
    17             #brust的意思就是,如果第1秒、2,3,4秒请求为19个,
    18             #第5秒的请求为25个是被允许的。
    19             #但是如果你第1秒就25个请求,第2秒超过20的请求返回503错误。
    20             #nodelay,如果不设置该选项,严格使用平均速率限制请求数,
    21             #第1秒25个请求时,5个请求放到第2秒执行,
    22             #设置nodelay,25个请求将在第1秒执行。
    23 
    24             limit_req zone=allips burst=5 nodelay;
    25             ...
    26         }
    27         ...
    28     }
    29     ...
    30 }
    31  
    nginx.conf

    1.3 问题

    攻击者可以通过代理池的方式来绕过

    2、WebApp限制单用户固定时间段的登陆频率

    2.1 作用

    极大的拖慢爆破的速度,通常一小时错误6次就要锁账号

    而且可以被锁定时发邮件提醒用户(感觉最多也就是让人心里有个数,但用没什么卵用)

    2.2 实现

    用户名密码等信息为了简化都保存在字典中,正常都应在数据库中

     1 # -*- coding: utf-8 -*-
     2 from flask import Flask,render_template,request,flash,redirect,url_for
     3 import datetime
     4 
     5 app = Flask(__name__)
     6 app.secret_key = 'zz'
     7 
     8 user_dict = {
     9     'admin':{
    10         'username': 'admin',
    11         'password': 'admin',
    12         'count': 0,
    13         'last_time': '',
    14         'new_time': '',
    15         'locked': False,
    16     },
    17     'test': {
    18         'username': 'test',
    19         'password': 'test',
    20         'count': 0,
    21         'last_time': '',
    22         'new_time': '',
    23         'locked': False,
    24     }
    25 }
    26 
    27 
    28 
    29 @app.route('/',methods=['POST','GET'])
    30 def hello_world():
    31     if request.method == 'POST':
    32         username = request.form.get('username')
    33         password = request.form.get('password')
    34         #验证用户是否存在,存在则继续,不存在则返回用户不存在
    35         if user_dict.get(username):
    36             #验证用户是否被锁
    37             if user_dict[username]['locked']:
    38                 #验证被锁时间是否达到3600秒,达到则用户登陆计数清零,锁定状态变为未锁定
    39                 if (datetime.datetime.now()-user_dict[username]['last_time']).seconds>3600:
    40                     user_dict[username]['count'] = 0
    41                     user_dict[username]['locked'] = False
    42                     flash('you are unlocked')
    43                     return redirect(url_for('hello_world'))
    44                 flash('you are locked')
    45                 return redirect(url_for('hello_world'))
    46             else:
    47                 if username==user_dict[username]['username'] and password==user_dict[username]['password']:
    48                     return 'ok'
    49                 else:
    50                     if user_dict[username]['count'] == 0:
    51                         user_dict[username]['count']+=1
    52                         user_dict[username]['last_time'] = datetime.datetime.now()
    53                         user_dict[username]['new_time'] = datetime.datetime.now()
    54                         flash('password is error')
    55                     elif user_dict[username]['count'] == 6:
    56                         user_dict[username]['new_time'] = datetime.datetime.now()
    57                         user_dict[username]['last_time'] = datetime.datetime.now()
    58                         user_dict[username]['locked'] = True
    59                         flash('you tried 6 times and you are locked 3600 seconds')
    60                     else:
    61                         user_dict[username]['count'] += 1
    62                         user_dict[username]['last_time'] = user_dict[username]['new_time']
    63                         user_dict[username]['new_time'] = datetime.datetime.now()
    64                         flash('password is error')
    65                     return redirect(url_for('hello_world'))
    66         else:
    67             flash('username errors')
    68             return redirect(url_for('hello_world'))
    69     return render_template('index.html')
    70 
    71 
    72 
    73 
    74 if __name__ == '__main__':
    75     app.run()
    app.py
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6 </head>
     7 <body>
     8 <form action="" method="post">
     9     username: <input type="text" name="username"><br>
    10     password: <input type="password" name="password"><br>
    11     <input type="submit"><label>count:{{ count }}</label>
    12 </form>
    13 <hr>
    14     {% for msg in get_flashed_messages() %}
    15     <p>{{ msg }}</p>
    16     {% endfor %}
    17 </body>
    18 </html>
    index.html

    2.3 问题

    可以故意输错密码让原账户登陆不了,造成原用户也无法使用

    3、扭曲的图片验证码

    3.1、作用

    为了防止重放攻击

    不能每次post和get都改变的验证码没有意义

    3.2、实现

    验证码的生成、获取、更新、核对全部在后端进行

    这里为了简化用户名密码没有加密保存在数据库中,验证码也没有生成图片显示

     1 from flask import Flask,render_template,request,session,flash,redirect,url_for
     2 import random
     3 
     4 app = Flask(__name__)
     5 app.secret_key = 'zz'
     6 
     7 user_dict = {
     8     'username':'admin',
     9     'password':'admin',
    10 }
    11 
    12 check_code_now = {
    13     'check_code':''
    14 }
    15 
    16 def get_code():
    17     ascii_num = [48,49,50,51,52,53,54,55,56,57]
    18     ascii_chr1 = [i for i in range(65,91)]
    19     ascii_chr2 = [i for i in range(97,123)]
    20     ascii = ascii_chr1+ascii_chr2+ascii_num
    21     random_pool = [chr(i) for i in ascii]
    22     check_code = random.choice(random_pool)+random.choice(random_pool)+random.choice(random_pool)+random.choice(random_pool)
    23     check_code_now['check_code'] = check_code
    24     return check_code
    25 
    26 
    27 @app.route('/',methods=['POST','GET'])
    28 def hello_world():
    29     if request.method == 'POST':
    30         username = request.form.get('username')
    31         password = request.form.get('password')
    32         check_code = request.form.get('check_code')
    33         if check_code_now.get('check_code')!= check_code:
    34             flash('check_code error')
    35             get_code()
    36             return redirect(url_for('hello_world'))
    37         if username == user_dict.get('username') and password == user_dict.get('password'):
    38             return 'ok'
    39         else:
    40             flash('username or password error')
    41             get_code()
    42             return redirect(url_for('hello_world'))
    43     check_code = get_code()
    44     return render_template('index.html',check_code = check_code)
    45 
    46 
    47 
    48 
    49 if __name__ == '__main__':
    50     app.run()
    app.py
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>Title</title>
     6 </head>
     7 <body>
     8 <form action="" method="post">
     9     username: <input type="text" name="username"><br>
    10     password: <input type="password" name="password"><br>
    11     checkcode:<input type="text" name="check_code"><label style="color: red">{{ check_code }}</label><br>
    12     <input type="submit">
    13 </form>
    14 <hr>
    15     {% for msg in get_flashed_messages() %}
    16     <p>{{ msg }}</p>
    17     {% endfor %}
    18 </body>
    19 </html>
    index.html

    3.3、问题

    不要将验证码保存在session中

    不管是Flask(将session加密存储在浏览器cooikes中),还是Django(将session保存在数据库中,cooikes只保存sessionID)

    重放攻击中每次的session都不变,去session中核对验证码每次都是不变的,那验证码将没有意义

    另外扭曲的验证码会让用户都看不明白,而且某些图片识别还是能识别、、

    4、IP段黑白名单

    4.1 作用

    通过Web服务器(Nginx等)实现

    黑名单可以把某些IP直接Ban掉

    对内网的WebApp白名单很有用

    4.2 实现

    1 location / {
    2   deny    192.168.1.1;
    3   allow   192.168.1.0/24;
    4   allow   10.1.1.0/1
    5 }
    nginx.conf

    4.3 问题

    有些用户不知情的情况下被人当肉鸡,然后被黑名单Ban掉,影响正常使用、、

    5、写在最后

    还是那句老话,安全没有银弹

    这些措施加在一起也并不能防止代理池+图片识别工具,但可以大幅度增加爆破的时间

    然而很可能对用户正常使用产生影响、、

  • 相关阅读:
    四层、七层负载均衡的区别
    confd+etcd实现高可用自动发现
    从零开始搭建etcd分布式存储系统+web管理界面
    从零开始搭建Prometheus自动监控报警系统
    tcpdump工具使用说明
    Nginx的负载均衡
    Nginx的正向代理与反向代理详解
    linux集群自动化搭建(生成密钥对+分发公钥+远程批量执行脚本)
    linux文件权限总结(创建root不可以删除文件、只可追加的日志文件等)
    前端技巧备忘
  • 原文地址:https://www.cnblogs.com/cx59244405/p/10410746.html
Copyright © 2011-2022 走看看