Apache-2.4

apache RewriteRule 的語法以匹配 % 編碼的 URL?(修復字元編碼問題;windows-1252 <=> utf-8)

  • October 16, 2020

我託管了一個 URL 中包含“”的網頁,與託管靜態文件project²的磁碟目錄相匹配。project²

基於 java 的客戶端使用此頁面從 URL(生物資訊學軟體IGV)載入數據。我的頁面以http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam. 在瀏覽器中點擊這些連結將導致 IGV 客戶端(在 localhost 上執行)向GET http://example.org/project²/some/data/file.bam我的伺服器請求。

✅ Linux/Mac 上的 IGV 通過將此 URL 請求為 UTF-8 編碼²=來響應%C2%B2,一切正常。

❌我新獲得的Win-10使用者的客戶端請求²= %B2(windows-1252編碼),導致404-not-found。

在嘗試了幾十件事之後,我對如何幫助這個使用者束手無策。

我的印像是我應該能夠在伺服器端動態重寫錯誤編碼的 URL,以便它們最終仍然提供所需的數據,但我不知道使規則模式匹配的神奇字元組合轉義字元。


我已經嘗試過的事情

  • 仔細檢查 404 不是網路問題;我看到GET %B2ssl_access_log的 with404作為返回的狀態碼,所以它確實是伺服器在做它。

  • “正確”方式:在將 URL 提供給客戶端之前對 URL 進行 UrlEncoding。PerlURI::Encode encode_uri²變成%C3%82%C2%B2(顯然ò?)不知何故更錯誤

  • 三重檢查提供載入 URL 的網頁是否為 utf-8

    • 它提供標題Content-Type: text/html; charset=UTF-8
    • 設置AddDefaultCharset UTF-8httpd.conf
    • 似乎編碼資訊沒有從 webbrowser API-link-click 傳輸到 Java 程序中
  • 通過符號連結將目錄“加倍”~~projectª -&gt; project²~~並且project%B2 -&gt; project² ª是 UTF8 匹配%B2 編輯:ª 沒有任何關係;不知道我從哪裡得到的

  • 試圖以mod_rewrite幾種不同的方式將“壞”的 URL 變成好的 URL,但似乎都沒有:

RewriteEngine on
# RewriteRule Pattern Substitution [flags]
RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE] # encoded 'bad' request, unencoded redirect
RewriteRule (.*)²(.*) $1%C2%B2$2 [B,NE]            # config file is utf-8 encoded, so this is senseless.      
RewriteRule (.*)%B2(.*) $12$2 [B,NE]               # doesn't match?        
RewriteRule (.*)TZZT(.*) $1test$2                  # works, so RewriteEngine is working

RewriteRuleRewriteRuleFlags文件也不能幫助我理解我應該如何編碼Pattern-part 以便它可以工作:-(


類似的問題在這裡

解決方案

RewriteRules 必須使用\x而不是%為了匹配 % 編碼的 URL!(字節序列的 PCRE 語法)

mod_rewrite-config 使用 PCRE 正則表達式語法,並對解碼的 URL 進行操作,因此%在模式中鍵入 -encodingRewriteRule會導致它查找文字%-character,而不是編碼值。

RewriteRules 中正確的轉義字元是,因此可以使用(或不區分大小寫)匹配\xURLencoded 值。%B2``\xb2``\xB2

請注意,這RewriteRule是一種針對字元編碼問題的 hacky 解決方案,它僅在恰好有一個特定的錯誤編碼字元位於特定的、可預測的位置時才有效。

有關任意位置多個錯誤編碼字元的通用解決方案,請參閱Apache .htaccess 能否將編碼 URI 中的百分比編碼從 Win-1252 轉換為 UTF-8?,它提出了一種通用解決方案,該解決方案使用RewriteMap功能齊全的程式語言耦合到外部程序。

正確的解決方案仍然是從源頭上防止這種情況,在整個鏈中使用顯式的 %-encoding。這可以避免依賴於作業系統的編碼意外發生在您無法控制的“中間某處”。(假設路徑上沒有客戶端進行雙重編碼,這應該是一種應受懲罰的罪行..)


我是怎麼到這裡的

絕望了,我按照mod_rewrite docsLogLevel Warn rewrite:trace3中的建議提高了伺服器範圍的日誌記錄。這被警告(嚴重)影響伺服器性能,但由於這是一個低流量的伺服器,並且沒有預先存在的重寫,所以可以管理。

額外的日誌記錄被發送到 ( ssl_)error_log中。這讓我深入了解瞭如何嘗試匹配,以及規則和 URI 的內部表示在mod_rewrite.

摘自ssl_error_log(為簡潔起見省略了許多列),帶有規則RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE,L]

[rewrite:trace3] applying pattern '(.*)project%B2/(.*)' to uri 'project\xb2/'
[rewrite:trace1] pass through /var/www/html/example.org/project\xb2

請注意,來自客戶端的 request-uri 是寫的\xb2,但我的模式使用%B2.

使用規則將規則語法與 uri 語法匹配RewriteRule (.*)project\xB2/(.*) $1project²/$2 [NE,L]

[rewrite:trace3] applying pattern '(.*)project\\xb2/(.*)' to uri 'project\xb2/'
[rewrite:trace2] rewrite 'project\xb2/' -&gt; 'project%c2%b2/'
[rewrite:trace1] internal redirect with /auth-test/project\xc2\xb2/ [INTERNAL REDIRECT]

🎉成功!🎉 如我們所見,我們現在正在匹配!


為什麼沒有[R]/[R=302]標誌?

由於這是一個字元編碼問題,我不認為進行額外的 HTTP 往返會增加價值;饋入客戶端的每個連結都會再次遇到相同的問題,除非我在將其饋入客戶端 java 程序之前修復了編碼問題。


不要忘記RewriteBase

請注意,這個縮短的版本省略了正確的設置RewriteBase,這可能會破壞重寫的路徑,具體取決於您conf寫入的位置(例如&lt;Directory&gt;vs &lt;Location&gt;)。沒有RewriteBase我不小心重定向到❌https://example.org/var/www/html/rewrite-testing/project² 而不是✅ https://example.org/rewrite-testing/project²

您不能僅使用 mod_rewrite “轉換編碼”,但是,您可以在請求的 URL 中搜尋特定的字元序列並“更正它”。

http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam
RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE]

請注意,它project²作為查詢字元串的一部分出現在您發布的範例 URL 中,但是,RewriteRule 模式(您在上面使用的)僅與 %-decoded URL 路徑匹配(不包括查詢字元串)。要匹配查詢字元串,您需要使用附加RewriteCond指令並匹配QUERY_STRING(或THE_REQUEST)伺服器變數。

請注意,QUERY_STRING(and THE_REQUEST) 伺服器變數是 % 編碼的(或者更確切地說,是從客戶端發送的) - 它們沒有經過 % 解碼。

請嘗試以下操作:

RewriteCond %{QUERY_STRING} (.+)/project%B2/(.*)
RewriteRule ^(load)$ $1?%1/project%C2%B2/%2 [NE,L]

反向引用%1替換%2字元串中的引用前面的CondPattern - 麻煩部分之前和之後的部分。/project%B2/

$1只是對 URL 路徑的反向引用(以保存重複),我假設它總是load.

NE標誌防止%自身(當用作 URL 編碼字元的一部分時)被 URL 編碼。

**更新:**恐怕我最初的問題不清楚誰獲取哪個 URL,因此您的答案的“查詢字元串”部分不適用……

如果您需要匹配**% 編碼的 URL 路徑**,那麼您應該匹配THE-REQUEST伺服器變數。THE_REQUEST包含 HTTP 請求標頭的第一行並且未進行 % 解碼。它包含從客戶端發送的完整 URL 路徑(和查詢字元串)(以及請求方法和協議版本)。例如,對於格式錯誤的請求,格式如下的字元串:

GET /project%B2/some/data/file.bam HTTP/1.1

您可以匹配和更正如下:

RewriteCond %{THE_REQUEST} ^[A-Z]{3,7}\s(/project)%B2([^\s]+)
RewriteRule ^/?project %1%B2%C2%2 [NE,L]

%1並且%2是對前面CondPattern中擷取的子模式的反向引用。

另一方面,RewriteRule pattern僅與預處理的 %-decoded URL-path 匹配(如上所述)。那麼,%B2無論解碼為什麼;假設是 UTF-8 編碼。不幸的是,這是一個不可列印的字元,因此需要用正則表達式中的十六進製字元序列表示,即。\xb2(這是表示單個字節序列的 PCRE 語法)。

引用自:https://serverfault.com/questions/1036007