手冊:編輯令牌
编辑令牌(亦為 csrf 令牌)是一个由Mediawiki服务器在客户端执行更改页面的操作时生成的随机字符串。 客户端获得此编辑令牌后才可以编辑页面。编辑令牌用于确保用户真的想编辑页面,而不是误点击某个外部链接而不自觉地编辑了什么页面。另见跨站请求伪造的说明。
為什麼需要編輯令牌
編輯令牌用作執行更改時的額外安全措施。 如果僅使用cookie檢查用戶身份,則外部站點可以使用連結(類似以下的鏈接)來讓訪問者更改維基。
https://en.wikipedia.org/w/index.php?title=Image:Abcd.jpg&action=delete&oldimage=324242234
按照這樣的鏈接操作後,將導致管理員在不知不覺中請求刪除圖像。 如果管理員仍然处于登錄状态,服務器將檢查cookie並准許請求。
因此,会对内容产生变更的请求需要附加一小段信息作为HTTP参数,这段信息被称为编辑令牌。 编辑令牌嵌入在那些网页上的变更链接中——包括编辑表单(用户通过点击“发布更改”按钮来修改页面)、图片描述页(管理员可以发送请求删除一张图片的旧版本)和贡献记录(管理员可以执行回退操作)等处。
當用戶實際請求完成其更改時(即通過按下按鈕或点击鏈接),編輯令牌將被發送回服務器。 因為外部站點無權訪問用戶的編輯令牌,此举將向服務器證明用戶已直接從站點請求更改,而不是從外部站點請求更改。
工作原理
編輯令牌是存儲在PHP會話中的一串隨機字符串,其是存儲在服務器中的一個關聯數組,在各個会话之間使用cookie維護(例如:英語維基百科上的enwiki_session
)。
具体来说,編輯令牌存储在PHP会话的wsEditToken
元素中。
编辑令牌嵌入在网页上可以导致页面发生改变的链接中。
生成上述页面时,软件通过访问PHP会话的wsEditToken
元素获取编辑令牌(若此元素存在);否则生成一个随机字符串并存储于此元素中。
嵌入到网页中的东西并非元素wsEditToken
本身。
相对的,这个元素被拼接上一串被称作“盐”的字符串。“盐”与特定的操作和页面相关。返回的字符串经MD5散列算法处理后嵌入至网页内。
当用户发送请求时,这个字符串将作为HTTP参数发送回服务器。
服务器会检查该参数是否正确:按照先前描述的流程重新从PHP会话中生成字符串,检查其是否与客户端发送的参数一致。
過期
服務器返回的編輯令牌可以多次重複使用,以進行不同的編輯操作。 令牌僅在特定時間段內有效。 帶有過時令牌的API調用將返回「badtoken」的錯誤。 在這種情況下,必須在重試操作之前,從服務器獲取新的編輯令牌。
源代码
编辑令牌主要在User.php源代码文件通过以下的函数中处理:
- getEditToken(salt)
- 返回拼接上盐的PHP会话元素
wpEditToken
的MD5散列值。 如果这一元素在PHP会话中不存在,将生成一个随机字符串。 检索代码仓库中的getEditToken函数。 - matchEditToken(token, salt)
- 检查第一个参数对于给定的盐是否是一个有效的编辑令牌。函数会重复进行生成编辑令牌的操作,并比较生成的字符串和传入的第一个参数。具体来说,本函数首先调用
editToken(salt)
,之后将结果与传入的第一个参数进行比较。
盐
盐默认是一个空字符串,大部分操作使用盐的这一默认值。因此,服务器接收的用于执行默认操作的编辑令牌字符串也可在其它页面上执行其他操作。 然而,由于编辑令牌存储于PHP会话中,它只能在服务器上会话有效且客户端保存有对应会话令牌的cookie(如enwiki_session这一cookie)时使用。
僅當服務器和客戶端使用的盐相同時,使用盐生成的編輯令牌散列才可用於執行其他操作。 因此,如果盐与某个页面存在关联,那么在这个页面上用于执行某些操作的编辑令牌散列值就不可以被其他页面上的其他操作使用。
- 不使用默認空盐的操作是:
- rollback
- 盐是文章的標題(包括名稱空間前綴)與要還原其編輯的用戶名的連接;
- 刪除舊版本的圖像
- 盐是
oldimage
参数的值(在删除图像的所有版本时本参数为空,也就是默认盐的值) - Special:UserRights
- 此處,盐是要更改其屬性的用戶的用戶名;
- Special:Watchlist/clear
- 'clearwatchlist',盐是字符串
編輯令牌後綴
为避免通过存在故障的代理进行编辑,编辑令牌以+\
结尾。如果某个代理无法正确的处理反斜杠和加号,一般它也会弄乱wiki标记代码。
檢索客戶端
在1.18及其更高版本中,您不需要使用AJAX檢索編輯令牌,可使用mw.user.tokens.get( 'csrfToken' )
。
但請注意,您需要將mediawiki.user作為模塊的ResourceLoader依賴項。
建議您使用mw.api.postWithToken()
幫助程序的方法,該方法會在加載網頁後令牌已過期時,自動重試並處理。