Категорія: Gotchas
Що обрати для Client Storage: Registry чи Datasource
Марк Кругер докладно розповів про те, чому потрібно запобігати використанню реєстру для зберігання клієнтських змінних (client variables).
Він навів приклад, коли за кілька місяців (за умовчанням 90 днів) Adobe ColdFusion починає періодично очищувати реєстр (знову-таки, за умовчанням кожні 67 хвилин), що призводить до несподіваних "залипань" сервера.
Цікаво, що ця проблема стосується й Linux, де реєстр просто емулюється в текстовому файлі (/coldfusion8/registry/cf.registry).
Він слушно зауважив про те, що напевно запобігти використанню цих змінних дуже складно, навіть якщо розробник запевняє, що не користується ними.
Тож він пропонує використати окрему базу (MySQL чи SQL Server цілком підійдуть) та відповідне джерело даних (datasource) для зберігання клієнтських змінних -- з прикладами, та наводить кілька порад щодо ефективного використання цьго методу.
Насамкінець він розповідає про кілька проблем, що можуть спіткати на шляху впровадження цієї оптимізації, особливо коли це стосується налаштувань вже робочого серверу.
Про все це докладно та з прикладами читайте в дописі Марка.
Підхід до розгортання сайту на базі Fusebox 5
В нетрях обговорення груп Railo виявив пару повідомлень, інформацію з котрих варто зберегти на майбутнє. В них наводиться спосіб оновлень живого сайту на базі Fusebox 5.
Основною проблемою, що треба вирішити, це генерація parsed файлів при одночасному доступі кількох (багатьох) користувачів. Це є відомим недоліком FB, котрий поки що не було остаточно виправлено. Суть його в тому, що при конкурентному доступі FB може записати "криві" файли (наприклад, недописані, коли <cfif> не закривається), через які весь додаток перестає працювати, видаючи незрозумілі й кожного разу різні помилки CFML синтаксису.
Тут є дві речі, що варто пам'ятати.
1. Необхідно завжди вмикати production mode на живому сайті. Незважаючи на очевидність кроку, це вельми розповсюджена помилка. Бо в development режимах FB генерує ті чи інші файли, що значно підвищує вірогідність виникнення помилок. Не кажучи вже про те, що сповільнює роботу сайту.
2. Варто тримати три робочі копії проекту.
Перша -- повноцінна розробницька версія, з відповідним development режимом, типово локально у розробника.
Друга -- живий сайт, з production режимом та всіма відповідними налаштуваннями.
І нарешті третя копія, призначення тільки для розгортання (deployment). Вона розміщується окремо від основного сайту (на тому самому сервері) та призначена для безпечного генерування parsed файлів (для великих проектів може займати кілька хвилин), котрі потім гуртом копіюються прямо на живо. Цю операцію варто повторювати при кожному оновленні, принаймні коли змінюються файли, що знаходяться під контролем Fusebox'у.
У випадку зміни ланцюжків (circuits) може потребуватися запустити основний сайт з fusebox.load, щоб він їх "підхопив".
На щастя, цей процес цілком можна автоматизувати, наприклад за допомогою Ant.
Примітка для користувачів Railo: deployment та production копії варто тримати ізольованими в різних веб-контекстах, хоча саме ядро Fusebox може бути те саме, достатньо використати мапінги (mapping) для контекстів.
Про деякі особливості Application.cfc
Не всі розробники задумуються над тим, що Application.cfc це не тільки Application, але й повноцінна CFC. Використання її саме у якості компоненти дає кілька цікавих можливостей.
Наприклад, розглянемо таку просту компоненту:
<cfcomponent output="false">
<cfset this.name = "application_introspect" />
<cfset this.foo = "bar" />
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfset application.thePage = arguments.thePage />
<cfreturn true />
</cffunction>
</cfcomponent>
А тепер спробуємо використати її.
<cfdump var="#Application#" >
<p> </p>
<cfset test = CreateObject("component", "Application") />
<!--- CF9 way
<cfset test= new Application() />
--->
<cfdump var="#test#">
<cfset test.onRequestStart("introspect.cfm") />
<p> </p>
<cfdump var="#Application#">
Таким нехитрим способом можна робити дві речі:
-
Подивитися що всередині об'єкту Application (на прикладі this.foo).
-
Зручно маніпулювати Application scope.
Результат роботи вищевказаного коду виглядатиме так:
Підміна MIME типу при завантаженні .cfm файлів на сервер
Днями прокотилася хвиля серйозних порушень роботи сайтів та навіть цілих ColdFusion серверів, пов'язана з використанням вразливості, коли підміна MIME типу завантажуваного файлу дозволяла залити .cfm скрипт та виконати його.
Ось простий приклад як це можна зробити:
<cfhttp url="http://target.example.com/upload" method="post">
<cfhttpparam file="#ExpandPath("badstuff.cfm")#"
mimetype="image/gif"
type="file"
name="photo">
</cfhttp>
Така проста конструкція легко дозволяє обійти вбудовану перевірку типу:
<cffile action="upload"
filefield="photo"
accept="image/gif,image/jpeg"
destination="#ExpandPath("./photos/")#">
Приклади було взято з посту Піта Фрейтаґа (Pete Freitag), в якому він наводить рекомендації щодо захисту від подібних атак.
Зокрема, можна використати вбудовані функції IsImageFile("path") (з урахуванням вже описаної проблеми) та IsPDFFile("path"), використовувати окремий сервер (простіше кажучи, окремий домен - скажімо з nginx - для швидкодії) для статичного вмісту (як то Amazon S3), користуватися можливостями Sandboxing (якщо у вас CF Enterise), відключати можливість виконання скриптів для тек завантаження та іншими методиками.
Ці та інші поради описані у вищевказаному повідомленні, також варто звернути увагу на коментарі до нього, там теж є слушні поради.
Також варто звернути увагу на аналогічне повідомлення Брента Фрая (Brent Frye), котрий зазнав серйозних неприємностей через описану проблему, тому теж уклав список порад для її обходу.
Подводные камни remote-методов
Организовать удаленный доступ к данным в Coldfusion - дело достаточно простое. Любой метод Coldfusion-компонента может вызываться удаленно, достаточно лишь поставить модификатор доступа access="remote", вот простой пример:
<cfcomponent displayname="myservice" hint="Service" output="false">
<cffunction name="getStructure" access="remote" output="false" returntype="Struct">
<cfset struct = StructNew() />
<cfset struct["id"] = 213 />
<cfset struct.refID = 321231 />
<cfset struct["someName"] = "Foo Bar" />
<cfreturn struct />
</cffunction>
<cffunction name="getQueryObject" access="remote" output="false" returntype="Query">
<cfquery datasource="test" name="qry">
SELECT id, someName, refID FROM test
</cfquery>
<cfreturn qry />
</cffunction>
</cfcomponent>
Однако на практике могут выскочить известные грабли.
- имена полей в объектах query возвращаются в UPPERCASE при вызове метода через SOAP;
- имена полей в объектах query возвращаются точно так как написано в коде, если метод вызывается через Flash Remoting (осторожно, флешеры!). При этом название поля в БД не имеет значения. Если поле в БД называется id, а программист в коде SQL запроса выбрал его как Id, во Flash Remoting отразится именно так. Как известно, ActionScript различает case символов, а значит могут быть проблемы.
- имена ключей в структурах возвращаются в UPPERCASE, если поле задано как struct.fieldName. При этом, case сохраняется в точности как задан, если поле задано как struct["fieldName"]. Сказанное справедливо для структур, возвращенных и через Web Service и через Flash Remoting.
Кириличні заголовки постів у MangoBlog
З переїздом на Mango у нас відразу виникла проблема: при створенні токенів (імен) для URL постів на базі заголовку видалялись кириличні символи. Це відбувалось тому, що блоґ намагався зробити безпечні посилання, а в таких знаходилося місце тільки літерам латинки, цифрам та дефісам.
Наприклад, спробувавши створити такий пост:
Ми отримували не дуже гарне посилання на цей пост:
Найбільш серйозною проблемою була втрата унікальності URL'ів, коли "Мій пост про MySQL" мав би майже таке саме посилання, що й "Пост не тільки про MySQL". Майже, бо блоґ додав би якесь випадкове число для запобігання співпадінь.
Почухавши потилицю, я вирішив виправити це діло. Півгодини копирсання в коді дали зрозуміти, що найбільш безболісним буде шлях транслітерації заголовку до його "чистки".
Читати далі...