Phase 9 — rollback миграции на host-IIS из-за Docker port-loop (host.docker.internal:80 от traefik container резолвится обратно в сам traefik через NAT, https.yml http-catchall middleware отдавал 301 -> TOO_MANY_REDIRECTS). Prod снова через VM. - iis-migration-2026-05-19-postmortem: 10 ошибок миграции + recipe для следующей попытки (backend port НЕ :80, smoke с MaximumRedirection 0, тест из НЕ-LAN, parallel VM x N часов, atomic revert plan) - webconfig-password-xml-escape: новая gotcha — & в conn-string пароле требует & в Web.config (XML reserved char) - iis-host-migration-2026-05-19: Phase 9 rollback chronology + что осталось на хосте inert - snolla-recovery-vm: статус -> active prod (обратно) - windows-recovery-host: host IIS sites -> inert artifacts - recovery-architecture-snapshot: chain снова через VM, traefik backends восстановлены из .bak-phase3 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.1 KiB
title, type, tags, sources, updated
| title | type | tags | sources | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Web.config — XML escape для спецсимволов в connection-string паролях | concept |
|
|
2026-05-19 |
Web.config: XML-escape для & (и других reserved chars) в пароле
Симптом
После patch Web.config с новым connection-string'ом ASP.NET-сайт отдаёт HTTP 500 на любой запрос. В Event Viewer / IIS logs — System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize или Unrecognized escape sequence. На уровне XML парсера — error при чтении Web.config.
Корень
Web.config — это XML-документ. Атрибуты в нём (<add connectionString="..." />) проходят через XML-парсер до того, как .NET runtime увидит сам connection string. Если в пароле есть XML-зарезервированный символ, парсер ломается.
XML reserved chars (в значениях атрибутов):
| Символ | XML-entity escape | Когда обязателен |
|---|---|---|
& |
& |
всегда (в attribute value и в text content) |
< |
< |
всегда |
" |
" |
если значение в "..." (наш кейс — атрибуты обычно в двойных кавычках) |
' |
' |
если значение в '...' |
> |
> |
формально не обязателен, но safer |
В именно нашем кейсе из iis-host-migration-2026-05-19: пароль ^I9D)LB)DK)8J#xBDG$t}W&_ioaT!M!LF содержит & — XML-парсер начинал интерпретировать &_ioaT... как entity reference (&_ioaT; — невалидное имя entity) → exception → IIS 500.
Правильный способ
<!-- WRONG (literal &) -->
<add name="MoreThenCmsEntities"
connectionString="...Password=^I9D)LB)DK)8J#xBDG$t}W&_ioaT!M!LF;..."
providerName="System.Data.SqlClient" />
<!-- RIGHT (& -> &) -->
<add name="MoreThenCmsEntities"
connectionString="...Password=^I9D)LB)DK)8J#xBDG$t}W&_ioaT!M!LF;..."
providerName="System.Data.SqlClient" />
После XML-парсинга .NET runtime получит литеральный & в значении атрибута и передаст в SqlConnection — там пароль уже текст, всё ок.
Что НЕ требует escape
^,$,(,),{,},[,],*,+,?,\,/,.,,,;,:,=,#,@,%,!,~,|— все безопасны в XML attribute value.- Особое:
;в пароле в ADO.NET conn-string'е требует обёртки пароля в'...'или"..."внутри connection-string'а — но это отдельная история.
PowerShell-подсказка для patch
При программной замене conn-string'а — сразу escape & в значение, не оставляй на потом:
# 💻 Windows host
$plainPassword = '^I9D)LB)DK)8J#xBDG$t}W&_ioaT!M!LF'
$xmlSafePassword = $plainPassword -replace '&', '&'
# и потом подставлять $xmlSafePassword в Web.config
# или комбинированный escape всех 5 reserved chars:
function Xml-EscapeAttr($s) {
$s -replace '&','&' -replace '<','<' -replace '>','>' -replace '"','"' -replace "'",'''
}
Связанные паттерны
- cms-config-rewrite-pattern — UTF-8 BOM ловушка при
Set-Contentбез-Encoding utf8(другая XML/IIS гнойная тема). - При генерации паролей для DB-логинов CMS — лучше запретить
&,<,"в генераторе, чтобы не словить эту проблему в будущем (особенно при автоматизированных patches Web.config через CI).