MySQL性能調優與架構設計(三)—— MySQL安全管理

前言

  1. 對於任何一個企業來講,其數據庫系統中所保存數據的安全性無疑是很是重要的,尤爲是公司的有些商業數據,可能數據就是公司的根本。
  2. 失去了數據,可能就失去了一切
  3. 本章將針對mysql的安全相關內容進行較爲詳細的介紹。

數據庫系統安全相關因素

一、外圍網絡

  1. Mysql的大部分應用場景都是基於網絡環境的,而網絡自己是一個充滿各類入侵危險的環境
  2. 因此要保護他的安全,在條件容許的狀況下,就應該從最外圍的網絡環境開始佈防,由於這一層防線能夠從最大範圍內阻止可能存在的威脅。
  3. 在網絡環境中,任意兩點之間均可能存在無窮無盡的道路能夠抵達,是一條真正「條條道路通羅馬」的環境。
  4. 在那許許多多的道路中,只要有一條道路不安全,就可能被入侵者利用。
  5. 固然因爲所處環境不一樣,潛在危險的來源也會不同。
  6. 有些mysql所處環境是暴露在廣域網中,能夠說是徹底裸露在任何能夠接入網絡環境的潛在威脅者面前。
  7. 而有些mysql是在一個環境相對小一些的局域網以內,相對來講,潛在威脅者也會少不少。
  8. 處在局域網以內的mysql,因爲有局域網出入口的網絡設備的基本保護,相對於暴露在廣域網中要安全很多,主要威脅對象基本控制在了能夠接入局域網的內部潛在威脅者,和極少數可以突破最外圍防線(局域網出入口的安全設備)的入侵者。
  9. 因此儘量的讓咱們的mysql處在一個有保護的局域網之中,是很是必要的。

二、主機

  1. 有了網絡設備的保護,咱們的MySQL就足夠安全了麼?我想你們都會給出否認的回答。
  2. 由於即便咱們局域網出入口的安全設備足夠的強大,能夠攔截住外圍試圖入侵的全部威脅者,但若是威脅來自局域網內部呢?
  3. 好比局域網中可能存在被控制的設備,某些被控制的有權限接入局域網的設備、以及內部入侵者等都仍然是威脅者。
  4. 因此說,即便是在第一層防線以內,咱們仍然存在安全風險,局域網內部仍然會有很多的潛在威脅存在。
  5. 這個時候就須要咱們部署第二層防線「主機層防線」了。
  6. 「主機層防線「主要攔截網絡(包括局域網)或者直連的未受權用戶試圖入侵主機的行爲。
  7. 由於一個惡意入侵者在登錄主機以後,可能經過某些軟件程序竊取到那些自身安全設置不夠健壯的數據庫系統的登錄口令,從而達到竊取或者破壞數據的目的。
  8. 如一個主機用戶能夠經過一個未刪除且未設置密碼的無用戶名本地帳戶輕易登入數據庫,也能夠經過mysql初始安裝好以後就存在的無密碼的「root@localhost」用戶登錄數據庫並得到數據庫最高控制權限。
  9. 非法用戶除了經過登入數據庫獲取或破壞數據以外,還可能經過主機上面相關權限設置的漏洞,跳過數據庫而直接獲取mysql數據(或日誌)文件達到竊取數據的目的,或者直接刪除(或者日誌)文件達到破壞數據的目的。

三、數據庫

  1. 經過第二道防線「主機層防線」的把守,咱們又能夠擋住很大一部分安全威脅者。
  2. 但仍然可能有極少數突破防線的入侵者。
  3. 並且即便沒有任何「漏網之魚」,那些有主機登入權限的使用者呢?是否真的就是徹底可信任對象?No,咱們不能輕易冒這個潛在風險。
  4. 對於一個有足夠安全意識的管理員來講,是不會輕易聽任任何一個潛在風險存在的。
  5. 這個時候,咱們的第三道防線「數據庫防線」就須要發揮做用了。
  6. 「數據庫防線「也就是mysql數據庫系統自身的訪問控制受權管理相關模塊。
  7. 這道防線基本上能夠說是mysql的最後一道防線了,也是最核心最重要的防線。
  8. 他首先須要抵擋住在以前的兩層防線都沒有卒攔住的全部入侵威脅,同時還要可以限制住擁有以前二層防線自由出入但不具有訪問權限的潛在威脅者,以確保數據庫 自身的安全以及所保存數據的安全。
  9. 以前的兩層防線對於全部數據庫系統來講基本上區別不大,都存在者基本相同的各類威脅,不管是Oracle仍是mysql,以及其餘的數據庫管理系統,都須要基本一致的「佈防」策略。
  10. 可是這第三層防線,也就是各自自身的「數據庫防線」對於每一個數據庫系統來講都存在較大差別,由於每種數據庫都有各自不太同樣的專門負責訪問受權相關的模塊。不管是權限劃分仍是實現方式均可能不太同樣。
  11. 對mysql來講,其訪問受權相關模塊主要是由兩部分組成。html

    (1)一個是基本的用戶管理模塊;另外一個是訪問受權控制模塊。
    (2)用戶管理模塊相對簡單,主要是負責用戶登錄鏈接相關的基本權限控制,但其在安全控制方面的做用沒必要任何環節小。
    (3)他就像mysql的一個「大門門衛」同樣,經過校驗每一位敲門者所給的進門「暗號」(登入口令),決定是非給敲門者開門。
    (4)而訪問控制模塊則是隨時隨地檢查已經進門的訪問者,校驗他們是否有訪問所發出請求須要訪問的數據的權限。
    (5)經過校驗者能夠順利拿到數據,而未經過校驗的訪問者,只能收到「訪問越權」的相關反饋。

四、代碼

  1. SQL語句相關安全因素:mysql

    (1)「SQL注入攻擊」指的就是攻擊者根據數據庫的SQL語句解析器的原理,利用程序中對客戶端所提交數據的校驗漏洞,從而經過程序動態提交數據接口提交非法數據,達到攻擊者的入侵目的。
    (2)「SQL注入攻擊「的破壞性很是大,輕者形成數據被竊取,重者數據遭到破壞,甚至可能丟失所有的數據。
  2. 程序代碼相關安全因素:sql

    (1)程序代碼若是權限校驗不夠仔細而存在安全漏洞,則一樣可能會被入侵者利用,達到竊取數據等目的。
    (2)好比一個存在安全漏洞的信息管理系統,很容易就可能竊取到其餘一些系統的登入口令。以後,就能冠冕堂皇的輕鬆登錄到其餘相關係統達到竊取相關數據的目的。
    (3)甚至還可能經過應用系統中保存不善的數據庫系統鏈接登錄口令,從而帶來更大的損失。

mysql權限系統介紹

一、權限系統簡介

  1. msql的權限系統在實現上比較簡單,相關權限信息主要存儲在幾個被稱爲grant tables的系統表中,即:mysql.User,mysql.db,mysql.Host,mysql.table_priv和mysql.column_priv。
  2. 因爲權限信息數據量比較小,並且訪問比較頻繁,因此mysql在啓動的時候,就會將全部的權限信息都load到內存中保存在幾個特定的結構中。
  3. 因此纔有了咱們每次手工修改了權限相關的表以後,都須要執行「flush privileges」命令從新加載mysql的權限信息。
  4. 固然,若是咱們經過grant、revoke或者drop user命令來修改相關權限,則不須要手工執行「flush privileges」命令,由於在使用grant等來修改系統表的同時,也會修改內存結構中的權限信息。
  5. 在mysql5.0.2或更高版本的時候,mysql還增長了CREATE USER命令,以此建立無任何特別權限(僅擁有初始USAGE權限)的用戶,經過CREATE USER命令建立了新用戶後,新用戶的信息也會自動更新到內存結構中。
  6. 因此,建議讀者通常狀況下儘可能使用GRANT、REVOKE、CREATE USER以及DROP USER命令來進行用戶和權限的變動操做,儘可能減小直接修改grant tables來實現用戶和權限變動的操做。

二、權限授予與去除

  1. 要爲某個用戶受權,可使用GRANT命令,要去除某個用戶已有的權限則使用REVOKE命令。
  2. 固然除了這兩個命令以外,還有一種比較暴力的辦法,那就是直接更新grant tables系統表。
  3. 當給某個用戶受權的時候,不只須要指定用戶名,同時還要指定來訪主機。
  4. 若是受權的時候僅指定用戶名,則mysql會自動認爲是對「username@%」受權。
  5. 要去除某個用戶的權限一樣也須要指定來訪主機。
  6. 可能有些時候咱們還須要查看某個用戶目前擁有的權限,這能夠經過兩個方式實現,首先是經過執行「show grants for 'username'@'hostname'」命令來獲取以前該用戶身上的全部受權。另外一種方法是查詢grant tables裏邊的權限信息。

三、權限級別

  1. Global Level:數據庫

    (1)Global Level的權限控制也叫全局權限控制,全部權限信息都保存在mysql.user表中。
    (2)Global Level的全部權限都是針對整個mysqld的,對全部的數據庫下的全部表及全部字段都有效。
    (3)若是一個權限是以Global Level來授予的,則會覆蓋其餘全部級別的相同權限設置。
    (4)好比咱們首先給abc用戶受權能夠update指定數據庫如test的t表,而後又在全局級別REVOKE掉了abc用戶對全部數據庫的全部表的UPDATE權限,那麼這時候的abc用戶將再也不擁有對test.t表的更新權限。
    (5)要授予Global Level的權限,則只須要在執行GRANT命令的時候,用「*.*」來指定適用範圍是Global的便可,當有多個權限須要授予的時候,也並不須要屢次重複執行GRANT命令,只須要一次將全部須要的權限名稱經過逗號(「,」)隔開便可,如:GRANT SELECT,UPDATE,DELETE,INSERT ON *.* TO 'def'@'localhost';
  2. Database Level:安全

    (1)Database Level是在Global Level之下,其餘三個Level之上的權限級別,其做用域即爲所指定整個數據庫中的全部對象。
    (2)與Global Level相比,Database Level主要少了如下幾個權限:CREATE USER、FILE、PROCESS、RELOAD、REPLICATION CLIENT、REPLICATION SLAVE、SHOW DATABASES、SHUTDOWN、SUPER和USAGE這幾個權限,沒有增長任何權限。
    (3)以前咱們說Global Level的權限會覆蓋底下其餘四層的相同權限,Database Level也同樣,雖然他可能被Global Level的權限所覆蓋,但同時他也能覆蓋比他更下層的Table、Column和Routine這三層的權限。
    (4)若是要授予Database Level的權限,則能夠有兩種實現方式:
        【1】在執行GRANT命令的時候,經過「database.*」來限定權限做用域爲database整個數據庫:GRANT ALTER ON test.* TO 'def'@'localhost';
        【2】先經過USE命令選定須要受權的數據庫,而後經過「*」來限定做用域,這樣受權的做用域實際上就是當前選定的數據庫
    (5)在授予權限的時候,若是有相同的權限須要授予多個用戶,咱們也能夠在受權語句中一次寫上多個用戶信息,用逗號分隔開就能夠了,如:grant  create  on  perf.*  to  'abc'@'localhost','def'@'localhost';
  3. Table Level:網絡

    (1)Database Level之下就是Table Level的權限了,Table Level的權限能夠被Global Level 和Database Level的權限所覆蓋,同時也能覆蓋Column Level 和Routine Level 的權限。
    (2)Table Level 的權限做用範圍是受權語句中所指定數據庫的指定表。如能夠經過以下語句給test數據庫的t1表受權:GRANT  INDEX  ON  test.t1  TO 'abc'@'%.jianzhaoyang.com'; 
    (3)Table Level的權限因爲其做用域僅限於某個特定的表,因此權限種類也比較少,僅有ALTER、CREATE、DELETE、DROP、INDEX、INSERT、SELECT、UPDATE這八種權限。
  4. Column Level:函數

    (1)Column Level的權限做用範圍就更小了,僅僅是某個表的指定的某個列。
    (2)因爲權限覆蓋的原則,Column Level的權限一樣能夠被Global、Database、Table這三個級別的權限中的相同級別所覆蓋,並且因爲Column Level所針對的權限和Routine Level的權限做用域沒有重合部分,因此不會有覆蓋與被覆蓋的關係。
    (3)Column Level的權限只有INSERT、SELECT和UPDATE這三種。
    (4)Column Level的權限受權語句語法基本和Table Level差很少,只是須要在權限名稱後面將須要受權的列名列表經過括號括起來:GRANT SELECT(id,value) ON test.t2 TO 'abc'@'%.jianzhaoyang.com'; 
    (5)當某個用戶在向某個表插入數據的時候,若是該用戶在該表中某列上面沒有INSERT權限,則該列的數據將以默認值填充。這一點和不少其餘的數據庫有一些區別,是mysql本身在SQL上面的擴展。
  5. Routine Level:工具

    (1)Routine Level的權限主要只有EXECUTE和ALTER ROUTINE兩種,主要的針對的對象是procedure 和function這兩種對象。
    (2)在授予Routine Level權限的時候,須要指定數據庫和相關對象,如:GRANT EXECUTE ON test.p1 to 'abc'@'localhost';
  6. GRANT權限:性能

    (1)除了上述幾類權限外,還有一個很是特殊的權限GRANT,擁有GRANT權限的用戶能夠將自身所擁有的任何權限所有授予其餘任何用戶,因此GRANT權限是一個很是特殊也很是重要的權限。
    (2)GRANT權限的授予方式也和其餘任何權限都不太同樣。
    (3)一般是經過在執行GRANT受權語句的時候在最後添加WITH GRANT OPTION子句達到授予GRANT權限的目的。
    (4)另外,咱們還能夠經過GRANT ALL語句授予某個Level的全部可用權限給某個用戶,如: grant all on test.t5 to 'abc';
  7. 以上五個Level的權限中,Table、Column和Routine三者在受權中所依賴(或引用的)對象必須是已經存在的,而不像Database Level的權限授予,能夠在當前不存在該數據庫的時候完成受權。

四、MySQL訪問控制實現原理

  1. MySQL的訪問控制實際上由兩個功能模塊共同組成,一個是負責「看守mysql大門」的用戶管理模塊,另外一個就是負責監控來訪者每個動做的訪問控制模塊。
  2. 用戶管理模塊決定造訪客人可否進門,而訪問控制模塊則決定每一個客人進門能拿什麼不能拿什麼。
  3. mysql中實現簡單訪問控制的流程圖以下:
    圖片描述
  4. 用戶管理:加密

    (1)在mysql中,用戶訪問控制部分的實現比較簡單,全部受權用戶都存放在一個系統表中:mysql.user,固然這個表不只存放了受權用戶的基本信息,還存放有部分細化的權限信息。
    (2)用戶管理模塊須要使用的信息不多,主要就是Host、User、Password這三項,都在mysql.user表中
    (3)一個用戶想要訪問mysql,至少須要提供上面列出的三項數據,mysql才能判斷是否該讓它進門。
    (4)這三項實際是由兩部分組成:來訪者來源的主機名(或主機IP地址信息)和訪問者的來訪「暗號」(登錄用戶名和登錄密碼),這兩部分中的任何一個沒有可以匹配上都沒法讓看守大門的用戶管理模塊乖乖開門。
    (5)其中Host信息存放的是mysql容許所對應的User的信任主機,能夠是某個具體的主機名或域名,也能夠是用「%」來充當通配符的某個域名集合,也能夠是一個具體的IP地址,一樣也能夠是存在通配符的域名集合,還能夠用「%」來表明任何主機,就是不對訪問者的主機作任何限制。
  5. 訪問控制:

    (1)當客戶端鏈接經過用戶管理模塊的驗證,可鏈接上mysql server以後,就會發送各類Query和Command給mysql server,以實現客戶端應用的各類功能。
    (2)當mysql接收到客戶端的請求以後,訪問控制模塊是須要校驗該用戶是否知足提交的請求所須要的權限。
    (3)權限校驗過程是從最大範圍往最小範圍的權限開始依次校驗所涉及到的每一個對象的每一個權限。
    (4)在驗證所需權限的時候,mysql首先會查找存儲在內存結構中的權限數據,首先查找Global Level權限,若是所需權限在Global Level都有定義(GRANT或REVOKE),則完成權限校驗(經過或者拒絕);
    (5)若是沒有找到全部權限的定義,則會繼續查找Database Level的權限,進行Global Level未定義的所需權限的校驗,若是仍然沒有找到全部所需權限的定義,則會繼續往更小範圍的權限定義域查找,也就是Table Level、Column Level或者Routine Level。
    (6)在前面咱們瞭解到mysql的grant tables有mysql.user、mysql.db、mysql.host、mysql.table_priv和mysql.column_priv這五個。
    (7)我想除了mysql.host以外的四個都很是容易理解,每個表都針對mysql的一種邏輯對象,存放某一特定Leve的權限,惟獨mysql.host稍有區別。
    (8)咱們來看看mysql.host權限表在mysql訪問控制中充當了什麼樣的角色?
        【1】mysql.host在mysql訪問控制模塊中所實現的功能比較特殊,和其餘幾個grant tables不太同樣。
        【2】首先mysql.host中的權限數據不是經過GRANT或REVOKE來授予或者去除,而是必須手工經過INSESRT、UPDATE和DELETE命令來修改其中的數據。
        【3】其次是其中的權限數據沒法單獨生效,必須經過和mysql.db權限表的數據一塊兒才能生效。
        【4】並且僅當mysql.db總存在不完整的時候,纔會促使訪問控制模塊再結合mysql.host中查找是否有相應的補充權限數據實現以達到權限校驗的目的。
        【5】在mysql.db中沒法找到知足權限校驗的全部條件的數據,則說明在mysql.db中沒法完成權限校驗,因此也不會直接校驗db.select_priv的值是不是Y。
    (9)mysql的權限授予至少須要用戶名和主機名兩者才能肯定一個訪問者的權限。
    (10)mysql如何肯定權限信息?實際上,mysql永遠優先考慮更精確範圍的權限。
    (11)在mysql內部會按照username和hostname作一個排序,對於相同username的權限,其host信息越接近訪問者的來源host,則排序位置越靠前,則越早被校驗使用到。
    (12)並且mysql在權限校驗過程當中,只要找到匹配的權限以後,就不會再繼續日後查找是否還有匹配的權限信息,而直接完成校驗過程。

mysql訪問受權策略

一、mysql訪問權限策略——前言

  1. 在咱們瞭解了數據庫系統安全的相關因素和mysql權限系統的工做原理以後,就須要爲咱們的系統設計一個安全合理的受權策略。
  2. 我想,每一個人都清楚,想要受權最簡單最簡便方便,維護工做量最少,那天然是將全部權限都授予全部的用戶來的最簡單方便了。
  3. 可是,一個用戶所擁有的權限越大,那麼他給咱們的系統所帶來的潛在威脅也就越大。
  4. 因此從安全方面考慮,權限天然是授予的越小越好。一個有足夠安全意識的管理員在受權的時候,都會只受權必要的權限,而不會授予任何多餘的權限。

二、受權策略:

  1. 咱們從安全的角度來考慮如何設計一個更爲安全合理的受權策略。
  2. 首先須要瞭解來訪主機:

    (1)因爲mysql數據庫登錄驗證用戶的時候是除了用戶名和密碼以外,還要驗證來源主機,因此咱們還須要瞭解每一個用戶可能從哪些主機發起鏈接。
    (2)固然,咱們也能夠經過受權的時候直接經過「%」通配符來給全部主機授予都有訪問的權限,可是這樣就違背了咱們安全策略的原則,帶來了潛在風險,因此並不可取。
    (3)尤爲是在沒有局域網防火牆保護的狀況下,更是不能輕易容許能夠從任何主機登錄的用戶存在。
    (4)能經過具體主機名和IP地址指定的儘可能經過使用具體的主機名和IP地址來限定來訪主機,不能用具體的主機名和IP地址限定的也須要用盡量小的通配範圍來限定。
  3. 其次,瞭解用戶需求:

    (1)既然要作到僅授予必要的權限,那麼咱們必須瞭解每一個用戶所擔當的角色,也就是說,咱們充分了解每一個用戶須要鏈接到數據庫上完成什麼工做。
    (2)瞭解用戶是一個只讀應用的用戶,仍是一個讀寫都有的用戶,是一個備份做業的用戶仍是一個平常管理的用戶,是隻須要訪問特定的數據庫,仍是須要訪問全部的數據庫。
    (3)只有瞭解了須要作什麼,才能準確的瞭解須要授予什麼樣的權限。
    (4)由於若是權限太低,會形成工做沒法正常完成,而權限太高,則存在潛在的安全風險。
  4. 再次,要爲工做分類:

    (1)爲了作到各司其職,咱們須要將須要作的工做分門別類,不一樣類別的工做使用不一樣的用戶,作好用戶分離。
    (2)雖然這樣可能會帶來管理成本方面的部分工做量增長,可是基於安全方面的考慮,這部分管理工做量的增長是很是值得的。
    (3)並且咱們所要作的分離也只是適度的分離。好比將執行備份工做、複製工做、常規應用訪問、只讀應用訪問和平常管理工做分別分理出單獨的特定帳戶來授予各自所需權限。
    (4)這樣,既可讓安全風險儘可能下降,也可讓同類同級別的類似權限合併在一塊兒,不互相交織在一塊兒。
    (5)對於PROCESS、FILE和SUPER這樣的特殊權限,僅僅只有管理類帳號才須要,不該該授予其餘非管理帳號。
  5. 最後,確保只有絕對必要者擁有GRANT OPTION權限:

    (1)以前在權限系統介紹的時候咱們已經瞭解到GRANT OPTION權限的特殊性,和擁有該權限以後的潛在風險,因此在這裏就不贅述了。
    (2)總之,爲了安全考慮,擁有GRANT OTPION權限的用戶越少越好,儘量只讓擁有超級權限的用戶才擁有GRANT OPTION權限。

安全設置注意事項

  1. 使用私有局域網。咱們能夠經過使用私有局域網,經過網絡設備,統一私有局域網的出口,並經過網絡防火牆設備控制出口的安全。
  2. 使用SSL加密通道。若是咱們對數據保密要求很是嚴格,能夠啓用mysql提供的ssl訪問接口,將傳輸數據進行加密。使網絡傳輸的數據即便被截獲,也沒法輕易使用。
  3. 訪問受權限定來訪主機信息。咱們能夠在受權的時候,經過指定主機的主機名、域名或IP地址信息來限定來訪主機的範圍。
  4. OS安全方面。關閉mysql Server主機上面任何不須要的服務,這不只能從安全方面減小潛在隱患,還能減小主機的部分負擔,儘量提升性能。
  5. 使用網絡掃描工具(如nmap等)掃描主機端口,檢查除了mysql須要監聽的端口3306以外,還有哪些端口是打開正在監聽的,並去沒必要要的端口。
  6. 嚴格控制OS帳號的管理,以防帳號信息外泄,尤爲是root和mysql帳號。
  7. 對root和mysql等對mysql的相關文件有特殊操做權限的OS帳號登錄後作出比較顯眼的提示,並在Terminal的提示信息中輸出當前用戶信息,以防止操做的時候通過屢次用戶切換後出現人爲誤操做。
  8. 用非root用戶運行mysql。由於若是使用root運行mysql,那麼myslqd的進程就會擁有root用戶所擁有的權限,任何具備FILE權限的用戶就能夠在mysql中向系統中的任何位置寫入文件。
  9. 文件和進程安全。合理設置文件的權限屬性,mysql的相關數據和日誌和所在文件夾屬主和所屬組都設置爲mysql,且禁用其餘全部用戶的讀寫權限。以防止數據或者日誌文件被竊取或破壞。
  10. 確保mysql server所在的主機上所必要運行的其餘應用或服務足夠安全,避免由於其餘應用或者服務存在安全漏洞而被入侵者攻破防線。
  11. 用戶設置。咱們必須確保任何能夠訪問數據庫的用戶都有一個比較複雜的內容做爲密碼,而不是很是簡單或者比較有規律的字符,以防止被使用字典破解程序攻破。
  12. 在 MySQL初始安裝完成以後,系統中可能存在一個不須要任何密碼的root用戶,有些版本安裝完成以後還會存在一個能夠經過localhost登陸的沒有用戶名和密碼的賬號。這些賬號會給系統帶來極大的安全隱患,因此咱們必須在正式啓用以前儘早刪除,或者設置一個比較安全的密碼。
  13. 對於密碼數據的存放,也不要存放在簡單的文本文件之中,而應該使用專業密碼管理軟件來管理(如KeePass)。
  14. 安全參數。不須要使用的功能模塊儘可能都不要啓用。例如,若是不須要使用用戶自定義函數,就不要在啓動的時候使用「--allow-suspicious-udfs」參數選項,以防止被別有居心的潛在威脅者利用此功能而對MySQL的安全形成威脅;
  15. 不須要從本地文件中Load數據到數據庫中,就使用「--local-infile=0」禁用掉能夠從客戶端機器上Load文件到數據庫中

參考連接

https://www.cnblogs.com/jesse...

相關文章
相關標籤/搜索