Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Подписывание Java апплета и некоторые тонкости java security

Anna | 3.06.2014 | нет комментариев
Постановка задачи:

В качестве WYSIWYG XML редактора в нашем приложении применяется java applet Oxygen Author Component. При загрузке этого апплета на заказчике Java не должна выкидывать никаких пугающих варнингов о небезопасном коде, а спокойно и тихо загружать себе апплет, не напрягая пользователя и не принуждая брать на себя тяжелую ответственность. У нас чай внушительное приложение как никак.

Пролог

Статья не претендует на научную выверенность и точность, допускаю какие-то некорректности в определениях. Википедию не открывал, пишу, так сказать, от сердца, но примечания принимаются.

Как работает апплет на пальцах

Браузер при выявлении тэга в HTML странице передает управление загрузке апплета соответствующему java-плагину, тот, что, в свою очередь передает управление JRE установленному на клиентской машине. Есть два метода загрузки апплета (под апплетом воспринимается некое java приложение, представляющее из себя комплект jar-ок, где есть main jar с Main class реализующим класс Applet):

  • в тэге Applet перечесляются jar файлы апплета, которые необходимо загрузить и откуда их грузить (codebase)
  • с поддержкой jnlp файла, в котором указывается эта информация, а также много других опций и доводов

В нашем случае мы используем jnlp. Превосходством jnlp подхода помимо прочих является то, что при изменении каких-то параметров загрузки апплета (скажем, codebase) не необходимо менять HTML либо JavaScript код, ответственный за загрузку апплета. Довольно легко поменять jnlp. Замена codebase (codebase — это url адрес указывающий на то где лежат jar-ки апплета) является даволно неприятной задачей, так как необходимо указывать безусловный URL, а это значит, что в зависимости от того на каком сервере запущен апплет, такой и должен быть codebase. На локальной машине это один адрес, на QA стенде иной, на продакшене 3-й, следственно при сборке приложения необходимо очевидно указывать Context Path, то есть безусловный адрес web-приложения, на котором оно будет трудиться.
Применение jnlp файла решает эту задачу тем, что есть особый сервлет, суть работы которого — динамически, на лету, менять codebase на нынешний при загрузке jnlp файла клиентской явой.

Поехали

Выходит, загрузка апплета начинается с загрузки jnlp файла, в котором помимо списка надобных jar файлов и их codebase указываются также доводы java (это дюже значимый момент, ниже узнаете отчего), с которыми эта java должна стартовать на клиентской машине. Java начинает загружать jar-ки и проверять их на безопасность. Здесь вступает в силу java security механизм, тот, что начинает проверку загружаемой жарки, раньше чем из нее будут загружены классы. Данный механизм давольно трудный и многогранный: настройка всеобщего яруса безопасности при загрузке апплетов и java security policy на заказчике (permission grants), параметры безопасности в манифестах самих jar, проверка верификация цифровой подписи jar и тд. Не хочу углублятся и буду затрагивать только те аспекты, которые главны в описываемой задаче.

Для справки: подписанние апплета подразумевает подписание jar-фалов. Подписание jar — это выполнение java-команды signjar, в итоге которой в jar-ке возникает информация о ключе, которым подписывали в зашифрованном виде, а также всякому источнику упакованному jar ставится в соответствие нейкий зашифрованный этим ключом код, содержащи в себе информацию о контенте данного источника. Таким, образом, если вы попытаетесь изменить подписанную жарку, подкинув скажем туда какой-нибудь класс, либо изменив ветхий, то такая жарка станет невалидной и она не пройдет проверку на security и не будет загружена.

Выходит, апплет может быть не подписанным, самоподписанным и подписанным доверенным сертификатом. В зависимости от версии клиентской явы, на которой апплет загружается, ярус подписанности апплета влияет на то будет ли апплет вообще загружен либо заблокирован, загружен но с кучей варнингов и сообщений типа «Если вы согласитесь запустить данный апплет, то наступит всеобщий катаклизм и вообще все пропадет, так что запускайте на свой ужас и риск»,

image

либо загружен с одним прекрасным и непугающим сообщением, которое дозволено не повторять в последующем нажав соответствующую галочку. Вот здесь мы подобрались к, собственно, задаче. На момент решения описываемой задачи наш Oxygen апплет был самоподписанным. е создается папка JNLP-INF и туда копируется наш начальный
author-component-dita.jnlp.
Дальше мы упаковываем все это в jar (это и есть наш main jar) и кидаем его в ту же папку lib рядом с остальными jar-ками.
Получается что теперь у нас есть 2 jnlp файла: начальный author-component-dita.jnlp и APPLICATION.JNLP упакованный в jar. Это меня немножко смущает, ну хорошо. Запускаю апплет — error!

image

Что на данный раз? Оказывается, что эти jnlp файлы не совпадают, а обязаны. Начальный jnlp применяется для загрузки апплета, а упакованный — для проверки подписи и они не обязаны отличатся. Но отчего они отличаются, они же копии? И здесь припоминаем наш Дивный сервлет (JnlpDownloadServlet ), тот, что применяется для упрощения деплоя вашего веб приложения — я теснее упоминал об этом выше. С поддержкой него дозволено не писать в jnlp определенный codebase (скажем, localhost:8888/oxygen-editor/), а применять переменную $$CODEBASE, а сервлет на рантайме сам изменяет jnlp подставляя в нее надобные значения переменных. Вот отчего загружаемый jnlp не совпадает с подписанным. Что же делать? Деплоить для различных адресов сервера различные варки? Это не наш путь. Все легко: необходимо применять APPLICATION-TEMPLATE.JNLP взамен APPLICATION.JNLP. Применение образца APPLICATION-TEMPLATE.JNLP отличается тем, что он может отличастся от начального jnlp, если взамен определенных значений параметров указывать “*”, скажем, codebase=”*”. Видоизменим наш антовский build.xml:


	<target name="compile">
		<mkdir dir="classes"/>
		<javac srcdir="src" destdir="classes" includeantruntime="false" debug="on">
			<classpath>
				<fileset dir="lib">
					<include name="*.jar"/>
				</fileset>
			</classpath>			
		</javac>

		<mkdir dir="classes/JNLP-INF"/>
		<copy file="author-component-dita.jnlp" tofile="classes/JNLP-INF/APPLICATION-TEMPLATE.JNLP" overwrite="true"/>
		<replace file="classes/JNLP-INF/APPLICATION-TEMPLATE.JNLP" token="@@CODEBASE@@" value="*"/>
		<replace file="classes/JNLP-INF/APPLICATION-TEMPLATE.JNLP" token="@@HREF@@" value="*"/>

	</target>

Выходит, неужто это все и я наконец увижу при загрузке апплета долгожданное user-friendly сообщение с синим щитом о том, что апплет доверенный, верный и не вызывает сомнений? Не веря в свое блаженство, дрожащими руками запускаю приложание, загружаю апплет и…

image

Ура! Свершилось! Я увидел наконец это сообщение с синим щитом! И разбрызгивая слюни радости я нажимаю галочку «Неизменно доверять этому паблишеру», оно закрывается, начинает грузится апплет и… здесь возникает это:

image

Что за …!? Быстро седея, начинаю судорожно втыкать в код — что же там такого несекьюрного? В всеобщем, я убил еще 2 дня, Дабы обнаружить вот эту строчку в jnlp файле:

<j2se java-vm-args="-Xmx512m -XX:MaxPermSize=80m -Xss4m -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5021" version="1.6 " />

Это java доводы, с которыми запускается апплет. Так вот, доводы дебага являются несекьюрными. И если ваш апплет подписан, причем доверенным сертификатом, эти доводы запрещены. Что увлекательно, если ваш апплет самоподписан, то запускайте апплет хоть с чертом безволосым, заказчик чай предупрежден, что исполняемое приложение неведомо, а значит первоначально небезопасно.

Вот полный список запрещенных доводов:

// note: this list MUST correspond to native secure.c file
private static String[] secureVmArgs = {
    "-d32",                         /* use 32-bit data model if available */
    "-client",                      /* to select the "client" VM */
    "-server",                      /* to select the "server" VM */
    "-verbose",                     /* enable verbose output */
    "-version",                     /* print product version and exit */
    "-showversion",                 /* print product version and continue */
    "-help",                        /* print this help message */
    "-X",                           /* print help on non-standard options */
    "-ea",                          /* enable assertions */
    "-enableassertions",            /* enable assertions */
    "-da",                          /* disable assertions */
    "-disableassertions",           /* disable assertions */
    "-esa",                         /* enable system assertions */
    "-enablesystemassertions",      /* enable system assertions */
    "-dsa",                         /* disable system assertione */
    "-disablesystemassertions",     /* disable system assertione */
    "-Xmixed",                      /* mixed mode execution (default) */
    "-Xint",                        /* interpreted mode execution only */
    "-Xnoclassgc",                  /* disable class garbage collection */
    "-Xincgc",                      /* enable incremental gc. */
    "-Xbatch",                      /* disable background compilation */
    "-Xprof",                       /* output cpu profiling data */
    "-Xdebug",                      /* enable remote debugging */
    "-Xfuture",                     /* enable strictest checks */
    "-Xrs",                         /* reduce use of OS signals */
    "-XX: ForceTimeHighResolution", /* use high resolution timer */
    "-XX:-ForceTimeHighResolution", /* use low resolution (default) */
    "-XX: PrintGCDetails",          /* Gives some details about the GCs */
    "-XX: PrintGCTimeStamps",       /* Prints GCs times happen to the start of the application */
    "-XX: PrintHeapAtGC",           /* Prints detailed GC info including heap occupancy */
    "-XX:PrintCMSStatistics",       /* If > 0, Print statistics about the concurrent collections */
    "-XX: PrintTenuringDistribution",  /* Gives the aging distribution of the allocated objects */
    "-XX: TraceClassUnloading",     /* Display classes as they are unloaded */
    "-XX:SurvivorRatio",            /* Sets the ratio of the survivor spaces */
    "-XX:MaxTenuringThreshol",      /* Determines how much the objects may age */
    "-XX:CMSMarkStackSize",
    "-XX:CMSMarkStackSizeMax",
    "-XX: CMSClassUnloadingEnabled",/* It needs to be combined with -XX: CMSPermGenSweepingEnabled */
    "-XX: CMSIncrementalMode",      /* Enables the incremental mode */
    "-XX:CMSIncrementalDutyCycleMin",  /* The percentage which is the lower bound on the duty cycle */
    "-XX: CMSIncrementalPacing",    /* Automatic adjustment of the incremental mode duty cycle */
    "-XX:CMSInitiatingOccupancyFraction",  /* Sets the threshold percentage of the used heap */
    "-XX: UseConcMarkSweepGC",      /* Turns on concurrent garbage collection */
    "-XX:-ParallelRefProcEnabled",
    "-XX:ParallelGCThreads",        /* Sets the number of parallel GC threads */
    "-XX:ParallelCMSThreads",
    "-XX: DisableExplicitGC",       /* Disable calls to System.gc() */
    "-XX: UseCompressedOops",       /* Enables compressed references in 64-bit JVMs */
    "-XX: UseG1GC",
    "-XX:GCPauseIntervalMillis",
    "-XX:MaxGCPauseMillis"          /* A hint to the virtual machine to pause times */
};

Спасибо за внимание, верю эта статья будет пригодной.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB