2020全網最全前端業務安全綜述

前言

上一篇面試總結中其實埋了不少坑,作到點到爲止,可是坑仍是須要埋的,今天這篇文章就是埋第一個坑。上篇總結中就有一個題目javascript

33.能說一下你項目中遇到了哪些安全問題麼,通常都是怎麼解決的?前端

xss、csrf、爬蟲、薅羊毛等安全問題

傳輸加密、接口加簽、環境變量、token、輸入校驗等java

那麼前端平時開發中涉及到哪些安全問題呢,又都是怎麼解決的呢,本文將一網打盡,同時建議各大中小公司,可以在公司內部實施的安全措施都應該實施起來。web

如下是正文

隨着前端的發展,前端應用正在迅速變化。 前端代碼承擔着與後端代碼幾乎相同的責任,能夠作更多的事情,隨着公司體系愈來愈完善,開發框架和平臺的不斷成熟,須要開發者考慮的安全問題愈來愈少,但並非開發者就不須要關心項目的安全問題。面試

本文主要介紹幾種業務開發中常常遇到的幾種前端安全問題,因爲篇幅有限本文點到爲止,後續有機會會逐一展開來說,本文提供大量的圖例來講明問題。數據庫

image

1. XSS

XSS是跨站腳本攻擊的簡寫,攻擊者想盡一切方法 將一段腳本內容放到目標網站的目標瀏覽器上解釋執行。攻擊者將惡意腳本輸入到目標網站中。 當其餘用戶訪問該網站的時候,因爲瀏覽器不知道它是由網站提供服務的腳本仍是攻擊者埋入的腳本,所以將執行此該腳本。攻擊者就能夠很容易利用埋入的腳本進行攻擊。後端

image

  1. 攻擊者編寫惡意攻擊的腳本
  2. 攻擊者訪問前端頁面,在輸入框中輸入編寫好的惡意腳本
  3. 攻擊者將惡意腳本進行提交,後端將惡意腳本存儲在數據庫中
  4. 當某些合法用戶訪問該網站的時候,該網站會獲取存儲在數據庫中的惡意腳本,可是瀏覽器不知道它是惡意腳本因此執行了。

其實就至關於攻擊者在用戶端的頁面上注入了一段腳本,有了這段腳本攻擊者就能夠隨心所欲了瀏覽器

防範
  1. 永遠不要相信用戶的輸入,對用戶輸入的特殊字符串進行轉譯,針對用戶的輸入設置標籤白名單
  2. cookie設置HttpOnly,配合token或驗證碼防範
  3. 設置CSP安全策略-能夠經過兩種方式設置CSP,一種是meta標籤,一種是HTTP響應頭Content-Security-Policy

2. CSRF

CSRF是跨站請求僞造的簡寫,一種誘騙受害者提交惡意請求的攻擊,攻擊者盜用了你的身份,以你的名義發送惡意請求,請求到達後端時,服務器將沒法區分惡意請求和合法請求。。CSRF可以作的事情包括:以你名義發送郵件,發消息,盜取你的帳號,甚至於購買商品,虛擬貨幣轉帳等。緩存

image

CSRF攻擊必須具有兩個流程安全

  1. 登陸受信任網站A,並在本地生成Cookie。
  2. 在不登出A的狀況下,訪問危險網站B。
防範
  1. 同源檢測,直接禁止外域(受信域能夠開白名單)對咱們發起請求
  2. CSRF Token,就把Token以參數的形式加入請求了,提交給服務器的時候,服務器須要判斷Token的有效性
  3. Samesite Cookie屬性,Samesite=Strict只容許同源網站提交請求攜帶cookie

3. 網絡傳輸安全

中間人 (Man-in-the-middle attack, MITM) 是指攻擊者與通信的兩端分別建立獨立的聯繫, 並交換其所收到的數據, 使通信的兩端認爲他們正在經過一個私密的鏈接與對方直接對話, 但事實上整個會話都被攻擊者徹底控制. 在中間人攻擊中, 攻擊者能夠攔截通信雙方的通話並插入新的內容。

是否是以爲有了https網絡傳輸安全問題就迎刃而解了呢,即便被中間人攔截了,數據也是加密的。其實不是這樣的,不知道你們有沒有使用過charles進行抓包呢,若是數據都是加密的,爲啥charles抓包後咱們可以看到傳輸的明文呢,其實這就是中間人攻擊。

image

charles中間人劫持

image

防範
  1. 對於我的來講防止本身被中間人攻擊最基本的就是不要亂連不信任的網絡
  2. 公司APP來講應該配置禁止被抓包
  3. APP和瀏覽器都應該嚴格校驗證書,不使用不安全的APP和瀏覽器

4. 接口加簽

經過上面的例子咱們知道https並非絕對安全的,他是會被中間人劫持的,那麼咱們有什麼方法防止數據被串改呢?

接口加簽的目的是防止數據被串改!

舉兩個例子

image

例子1:正經常使用戶提交轉帳申請,請求中攜帶正經常使用戶的用戶信息,他想轉帳N金額給用戶A,這樣的請求銀行無法拒絕會正常轉帳,由於攜帶了正常的用戶信息。可是當中間人劫持了這個請求,他修改了轉帳帳號爲B,修改了轉帳金額爲M,這樣咱們的錢會不會轉給其餘人呢?

image

例子2:咱們辛辛苦苦寫了一個運營小遊戲,違規用戶隨便玩了一下得分爲0,可是他經過Charles攔截了這個請求,修改了得分爲10000,而後進行提交,咱們的正常服務器可否知道分數是被串改的呢?

爲了解決上述問題,咱們能夠引入接口加簽

image

服務端網關首先會校驗籤是否是對的,若是不對直接拒絕請求,而籤的生成和請求參數密密相關,當接口請求中的參數被串改後,網關是無法進行驗籤經過的,直接拒絕了請求,拋出錯誤。

5. 接口加密

有時候咱們的參數根本不想被人看見是啥,咱們就能夠利用參數加密了

image

接口防重放

image

防重放也叫防複用,簡單來講,就是我獲取到這個請求的信息以後, 我什麼也不改, 我就拿着接口的參數去重複請求這個充值的接口,也就是說個人請求是合法的,由於全部參數都是跟合法請求如出一轍的,也就是說: 服務端的 sign 驗證必定能經過。如圖上的例子,即便咱們不知道登陸帳戶名密碼,即便接口參數被加簽加密了,咱們依舊可以登陸並拿到登陸信息,咱們根本不用關心加密加簽的邏輯,咱們只須要簡單的重放攻擊便可。

防重放設計
  1. 客戶端在請求中添加兩個參數
    1.1 添加一個隨機不重複的字符串參數 好比uuid 至於怎麼讓他不重複,能夠考慮拼接時間戳,md5隨機數等
    1.2 添加一個請求時間的參數 如 time 值就是發送請求時的 時間戳
  2. 服務端接收到請求以後:
    2.1 去緩存裏中查找 uuid 這個參數對應的值是否存在
    2.2 若是不存在: 就把這個uuid的值保存到緩存中, 記錄這個請求
    2.3 若是已存在: 存在那就證實, 已經請求過一次了, 就不處理這個請求了

這就是最簡單的防重放邏輯,接口只能調用一次,即便被中間人攻擊後也無法進行重放

image

6. 環境檢測

是否是瀏覽器

image

是否是咱們檢測上述變量就認爲是瀏覽器環境呢,其實不是這樣的,上面的變量都是能夠被串改的,因此能夠做爲參考,絕對不能過度的依賴!下面列舉幾項處理方案,能夠看到當咱們檢測這些變量的時候,這些變量都是能夠被串改的。

檢測變量 對抗處理方案
navigator.languages Object.defineProperty(navigator, 'languages', { get: () => ["zh-CN", "zh", "en"] });
navigator.plugins Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
是否是模擬器

image

通常咱們檢測到這些變量的時候能夠無腦的認爲就是模擬器,好比Puppeteer中咱們啓動的時候,navigator.webdriver這一屬性的值等於true的,常規瀏覽器中因爲沒有這個屬性navigator.webdriver的值等於undefined的。

Object.defineProperty(navigator, 'webdriver', {
    get: () => undefined,
});

攻擊者這樣串改後咱們是否是就沒有辦法知道是否是webdriver了呢?其實咱們仍是有辦法判斷的,由於這邊只是返回了navigator.webdriver的值是非的,可是navigator上依舊有webdriver這個屬性,咱們有沒有辦法檢測屬性是否存在呢?其實咱們很容易拿到navigator上全部屬性的。

var attr = window.navigator, result = [];
do {
    Object.getOwnPropertyNames(attr).forEach(function(a) {
        result.push(a)
    })
} while (attr=Object.getPrototypeOf(attr));

當咱們判斷navigator上有webdriver這個屬性的時候,就能夠簡單的認爲這個是模擬器環境,是否是以爲很完美的判斷了是否是模擬器了,其實不是的,攻擊者甚至能夠刪除掉webdriver屬性。

delete navigator.__proto__.webdriver

這樣以後就徹底抹去webdriver變量了,經過這個辦法來判斷是否是模擬器就沒有路子了。

有沒有用戶行爲

image

一般咱們能夠經過判斷事件上的isTrusted屬性來判斷是否是真實的事件,大部分狀況咱們都可以很好的處理,可是攻擊者是很可怕的,這些簡單的伎倆他們可以輕輕鬆鬆的繞過,他能夠重寫事件啊,好比:

function clone(e) {
    const t = {};
    for (let attr in e) {
        if (typeof e[attr] === "function") {
            t[attr] = e[attr];
        } else {
            Object.defineProperty(t, attr, {
                get: () => {
                    if (attr === 'isTrusted') {
                        return true;
                    }
                    return e[attr];
                },
                set: v => {
                    e[attr] = v;
                }
            });
        }
    }
    return t;
}
const oldAEL = document.addEventListener;
window.addEventListener = document.addEventListener = function (e, func, c) {
    const newFunc = function (event) {
        const newEvent = clone(event);
        return func(newEvent);
    };
    return oldAEL.call(this, e, newFunc, c);
};

經過上面的例子咱們發現,無論咱們怎麼攻防,攻擊者都是有辦法繞過去的。其實上面還都是簡單的攻防,攻擊者甚至能夠本身定製瀏覽器,當咱們的頁面跑在攻擊者定製的瀏覽器中的時候,經過上面的那些方法咱們真的無能爲力了,那麼是否是咱們只能放棄了呢,其實不是的。

辨別機器行爲仍是得須要驗證碼

7. 無處不在的驗證碼

驗證碼這個名詞真正被髮明出來是在2003年,這比不少概念晚多了,好比神經網絡70年代就已經有不少人在研究。卡內基梅隆大學的Luis von Ahn,Manuel Blum, Nicholas J.Hopper等人首次提出了「CAPTCHA」這個詞。他們對驗證碼系統作的很深入的研究,而且將其付諸程序化。自此大量的驗證碼開始被應用到網站中,有效的阻止了黃牛軟件的肆虐。時至今日,天天有過億的驗證碼被人們不斷地輸入着。

傳統驗證碼

image

傳統驗證碼易被各圖像識別軟件、打碼平臺輕易破解,人工智能飛速發展,所以扭曲的文本驗證方式也再也不是一個可靠的方法,聽說已經可以解決99.8%的圖片字符型驗證碼。由此誕生了不少新型的驗證碼類型,其中國內最具表明的就是極驗,國外的就是谷歌的reCAPTCHA,他們帶來了一種全新的模式。

新型驗證碼

image

新型驗證碼不只很難破解,他的交互會更加的友善,甚至作到無驗證碼,只有在須要進行驗證的時候纔出來。下面是網易易盾的產品流程圖,其餘產品都基本相似。背後依託強大的機器學習判斷行爲究竟是不是人。

image

8. 代碼加密混淆

image

代碼加密混淆大大下降了前端代碼的可讀性,同時必定程度上會增長代碼的體積。可是對於很是核心的業務邏輯,代碼加密是很是有必要的,好比:

  1. 前端加簽代碼,因爲加簽是在前端進行的,前端必須存有祕鑰和加簽規則,可是一旦被第三方知道加簽的祕鑰和規則,加簽也就不公而破了,因此加簽的前端代碼必須得加密。
  2. 新型驗證碼用戶行爲採集代碼,新型驗證碼涉及不少用戶行爲的前端採集,而後提交後端分析,若是採集規則被第三方知道,那麼攻擊者也就很好的進行攻擊行爲,因此採集代碼也是須要加密的。

歡迎關注公衆號:前端複習課,一塊兒分享交流前端知識

image

相關文章
相關標籤/搜索