1. 程式人生 >實用技巧 >其它 >Flask安全 - 副本

Flask安全 - 副本


Flask 作為一個 WEB 框架來說內部封裝的安全性的措施很多,基本解決了常見的 web 漏洞,下面介紹 Flask 是如何來避免產生 常見的 web 漏洞。

SQL注入:

Flask 使用 ORM 物件關係對映的資料庫管理方式,同時 Flask 框架內部也封裝了許多安全性的函式,對於 SQL 注入擁有一定防護力,如圖

Flask安全 - 副本


Flask 中單引號會自動進行轉義

XSS:

Flask 使用 Jinja2 模板引擎,Jinja 會預設將渲染變數進行 HTML 實體轉義

@user_bp.route('/test2')
def test2():
    code = '<h1>Flask</h1>'
    return render_template('test.html', code=code)

# test.html
{{ code }}

Flask安全 - 副本


如果需要讓 HTML 標籤生效 可以使用 safe 過濾器

例如:一些富文字編輯器以 HTML 格式進行文章的儲存,此時需要加 safe 過濾器保證文章正常儲存顯示,此時 xss 潛在發生處就是富文字編輯器

Flask安全 - 副本


Flask 配置 Jinja2 自動轉義所有值,除非顯式地指明不轉義。這就排除了模板導致的所有 XSS 問題,但是你仍需要在其它的地方小心

  • 生成 HTML 而不使用 Jinja2
  • 在使用者提交的資料上呼叫了 Markup
  • 傳送上傳的 HTML 檔案,永遠不要這麼做,使用 Content-Disposition: attachment 標頭來避免這個問題
  • 傳送上傳的文字檔案。一些瀏覽器使用基於開頭幾個位元組的 content-type 猜測,所以使用者可能欺騙瀏覽器執行 HTML

雖然 Jinja2 可以通過轉義 HTML 來保護你免受 XSS 問題,仍有一種情況,它不能保護你: 屬性注入的 XSS 。為了應對這種攻擊媒介,確保當在屬性中使用 Jinja 表示式時,始終用單引號或雙引號包裹屬性

正確:<a href="{{ href }}">the text</a>
錯誤:<a href={{ href }}>the text</a>
# 官方文件所指明的,在實際測試中不論加沒加引號,瀏覽器解析時都會有引號

# 注意儘量要讓使用者去控制 href 屬性
@article_bp1.route('/test')
def article_test():
    html = '''
    <h1>111111</h1>
    '''
    href ='javascript:alert(document.body);'
    return render_template('article/test.html', html=html, href=href)

http://127.0.0.1:5000/article/test?href=javascript:alert(document.cookie);

SSTI:

當用戶輸入被串聯到模板中而不是作為資料傳遞時,伺服器端模板注入漏洞就會出現,簡單來說也就是不正確的使用 flask 中的render_template_string 方法會引發SSTI

確定模板引擎

常用的方法是使用來自不同模板引擎的語法注入任意數學運算

Flask安全 - 副本

存在漏洞程式碼示例

from flask import Flask,render_template_string,request

app = Flask(__name__)
@app.route('/test/')
def test():
    code = request.args.get('id')   //get方式獲取id
    html = '''
        <h3>%s</h3>
    '''%(code)
    return render_template_string(html)
#產生原因就是先進行了拼接,在渲染
app.run()

這個問題也並非是Jinja2的問題,而且在編寫的過程中出現的人為問題

Flask安全函式

flask 中有許多提高安全性的函式,比如以下幾個

generate_password_hash(password)
# 將 password 變數的值進行加鹽 hash 加密
check_password_hash(user.password, password)
# 與上個函式配合使用,用來將 加密資料與為加密資料做對比,如果未加密資料加密後為已加密資料則返回 true,否則返回 false
secure_filename(icon_filename)
# 將傳入檔案的檔名進行安全處理,如將空格替換為下劃線等

CSRF:

Flask 中並不存在表單驗證,很容易造成 CSRF

基本上,對於每個修改伺服器上內容的請求,應該使用一次性令牌,並存儲在 cookie 裡, 並且在傳送表單資料的同時附上它,在伺服器再次接收資料之後,你要比較兩個令牌,並確保它們相等

Flask 使用 Flask-WTF 外掛來避免出現 CSRF

使用方法

# 後端
class UserForm(FlaskForm):
    username = StringField(label='使用者名稱', validators=[DataRequired(), Length(min=6, max=20, message='長度必須在6~20位之間')])
    password = PasswordField(label='密碼', validators=[DataRequired(), Length(min=6, max=20, message='長度必須在6~20位之間')])
    confirm_password = PasswordField(label='確認密碼',
                                     validators=[DataRequired(), Length(min=6, max=20, message='長度必須在6~20位之間'),
                                                 EqualTo('password', '密碼不一致')])
    phone = StringField(label='手機號', validators=[DataRequired(), Length(min=11, max=11, message='長度必須是11位')])
    email = EmailField(label='郵箱', validators=[DataRequired()])
    recaptcha = StringField(label='驗證碼')

    def validate_recaptcha(self, data):
        input_code = data.data
        code = session.get('valid')
        if input_code.lower() != code.lower():
            raise ValidationError('驗證碼錯誤!')
......            
       

# 前端
 <form class="form-horizontal" id="container" method="post" action="{{ url_for('user.register') }}">
        {{ userform.csrf_token }}
        {#  防止csrf,必須設定secret_key  #}
        <table id="register">
            <tr>
                <td>{{ userform.username.label }}:</td>
                <td>{{ userform.username }}{% if userform.username.errors %}
                    {{ userform.username.errors.0 }}{% endif %}<br>
                    {#  如果有報錯則輸出報錯的message, .0表示只輸出內容  #}</td>
            </tr>
......                        

JSON安全:

Flask 使用 jsonify 函式將資料轉換為 json 資料返回

# 此段程式碼為後臺驗證碼校驗程式碼
@user_bp.route('/check_phone', methods=['GET', 'POST'], endpoint='check_phone')
def check_phone():
    phone = request.args.get('phone')
    user = User.query.filter(User.phone == phone).all()
    # code:200可用  400不可用
    if len(user) > 0:
        code = 400
    else:
        code = 200
    return jsonify(code=code)

開發文件:http://docs.jinkan.org/docs/flask/security.html