Ngnix限制靜態(tài)文件訪問

Ngnix,一個高性能的web服務(wù)器纳胧,毫無疑問它是當(dāng)下的寵兒镰吆。卓越的性能,靈活可擴展躲雅,在服務(wù)器領(lǐng)域里攻城拔寨鼎姊,征戰(zhàn)天下。

靜態(tài)文件對于大多數(shù)website是不可或缺的一部分相赁。使用Nginx來處理靜態(tài)文件也是常見的方式相寇。然而,一些靜態(tài)文件钮科,我們并不像任何情況下都公開給任何用戶唤衫。例如一些提供給用戶下載的文件,一些用戶上傳的涉及用戶隱私的圖片等绵脯。我們我希望用戶登錄的情況下可以訪問佳励,未登錄的用戶則不可見。

粗略的處理蛆挫,在后端程序可以做過濾赃承,渲染頁面的時候,在視圖邏輯里面驗證用戶登錄悴侵,然后返回對應(yīng)的頁面瞧剖。例如下面的flask代碼(偽代碼)

@app.router('/user/idcard'):
def user_idcard_page():
    if user is login:
        return '<img src="/upload/user/xxx.png'>"
    else:
        reutrn '<p>Pemission Denied<p>', 403

可是這樣的處理,還有一個問題可免,靜態(tài)文件是交給 nginx 處理的抓于,如果hacker找到了文件的絕對地址,直接訪問 http://www.example.com/upload/user/xxx.png也是可以的浇借。恰巧這些文件又涉及用戶隱私捉撮,比如用戶上傳的身份證照片。那么碼農(nóng)可不希望第二天媒體報道妇垢,知名網(wǎng)站XXX存在漏洞巾遭,Hacker獲取了用戶身份證等信息。

為了做這樣的限制闯估,可以借助 Nginx 的一個小功能----XSendfile恢总。 其原理也比較簡單,大概就是使用了請求重定向睬愤。

我們知道片仿,如果用Nginx做服務(wù)器前端的反向代理,一個請求進來尤辱,nginx先補捉到砂豌,然后再根據(jù)規(guī)則轉(zhuǎn)發(fā)給后端的程序處理厢岂,或者直接處理返回。前者處理一些動態(tài)邏輯阳距,后者多是處理靜態(tài)文件塔粒。因此上面那個例子中,直接訪問靜態(tài)文件的絕對地址筐摘,Nginx就直接返回了卒茬,并沒有調(diào)用后端的 user_idcard_page做邏輯限制。

為了解決這個問題咖熟,nginx提供的 XSendfile功能圃酵,簡而言之就是用 internal 指令。該指令表示只接受內(nèi)部的請求馍管,即后端轉(zhuǎn)發(fā)過來的請求郭赐。后端的視圖邏輯中,需要明確的寫入X-Accel-Redirect這個headers信息确沸。

偽代碼如下:

location /upload/(.*) {
        alias /vagrant/;
        internal;
}

@app.router('upload/<filename>')
@login_required
def upload_file(filename):
    response = make_response()
    response['Content-Type'] = 'application/png'
    response['X-Accel-Redirect'] = '/vagrant/upload/%s' % filename
    return response

經(jīng)過這樣的處理捌锭,就能將靜態(tài)資源進行重定向罗捎。這樣的用法還是比較常見的,很多下載服務(wù)器可以通過這樣的手段針對用戶的權(quán)限做下載處理桨菜。

Flask是我喜歡的web框架,F(xiàn)lask甚至實現(xiàn)了一個 sendfile的方法雷激,比上面的做法還簡單。我用vagrant作了一個虛擬機告私,用Flask實現(xiàn)了上面的需求屎暇,具體代碼如下:

項目結(jié)構(gòu)
project struct

project
  app.py
  templates
  static
    0.jpeg
  upload
    0.jpeg

nginx的配置 nginx conf

web.conf

server {
        listen 80 default_server;
        
        # server_name localhost;
        server_name 192.168.33.10;
        location / {
                proxy_pass http://127.0.0.1:8888;
                proxy_redirect off;
                proxy_set_header Host $host:8888;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        # 正常的靜態(tài)文件
        location /static/(.*) {
                root /vagrant/;

        }
        # 用戶上傳的文件驻粟,需要做權(quán)限限制
        location /upload/(.*) {
                alias /vagrant/;
                internal;             # 只接受內(nèi)部請求的指令
        }
}

Flask 代碼

app.py

from functools import wraps
from flask import Flask,  render_template, redirect, url_for, session, send_file

app = Flask(__name__)

app.config['SECRET_KEY'] = 'you never guess'

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not session.get('login'):
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

@app.route('/')
def index():
    return 'index'


@app.route('/user')
@login_required
def user():

    return render_template('upload.html')

# 用戶上傳的文件視圖處理,在此處返回請求給nginx
@app.route('/upload/<filename>')
@login_required
def upload(filename):

    return send_file('upload/{}'.format(filename))


@app.route('/login')
def login():
    session['login'] = True
    return 'log in'

@app.route('/logout')
def logout():
    session['login'] = False
    return 'log out'


if __name__ == '__main__':
    app.run(debug=True)

簡單部署

gunicorn -w4 -b0.0.0.0:8888 app:app  --access-logfile access.log --error-logfile error.log

代碼示例 https://github.com/rsj217/flask--scaffold/tree/master/nginx-x-sendfile

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜀撑,一起剝皮案震驚了整個濱河市挤巡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酷麦,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件母廷,死亡現(xiàn)場離奇詭異轻黑,居然都是意外死亡琴昆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門抖拦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人态罪,你說我怎么就攤上這事∠蛲危” “怎么了诸狭?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長驯遇。 經(jīng)常有香客問我,道長叉庐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任玩郊,我火速辦了婚禮枉阵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兴溜。我一直安慰自己,他們只是感情好拙徽,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布刨沦。 她就那樣靜靜地躺著膘怕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侧蘸,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天讳癌,我揣著相機與錄音穿稳,去河邊找鬼晌坤。 笑死,一個胖子當(dāng)著我的面吹牛骤菠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播央拖,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鹉戚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抹凳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤失都,失蹤者是張志新(化名)和其女友劉穎幸冻,沒想到半個月后粹庞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洽损,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年强缘,在試婚紗的時候發(fā)現(xiàn)自己被綠了不傅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赏胚。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖觉阅,靈堂內(nèi)的尸體忽然破棺而出秘车,到底是詐尸還是另有隱情劫哼,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布权烧,位于F島的核電站,受9級特大地震影響妻率,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宫静,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一券时、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧革为,春花似錦、人聲如沸震檩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迂猴。三九已至慕淡,卻和暖如春沸毁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背息尺。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徐紧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓并级,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嘲碧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容