Permalink
Browse files

Enforced access controls on some administrative actions.

 - ensure use of HTTP POST method : HTTP GET should only be used for
information retrieval and not to perform server side effect operations
(see HTTP standard https://tools.ietf.org/html/rfc7231#section-4.2.1)
 - a transaction token is now required for these administrative form
submissions to ensure the request can not be included in an external
site and performed silently/by mistake by the user browser
  • Loading branch information...
luccioman committed Mar 26, 2017
1 parent df5970d commit cde237b68763c542da20038e5f62bea341ae1d37
@@ -1,3 +1,3 @@
#!/usr/bin/env sh
cd "`dirname $0`"
./apicall.sh "/IndexControlURLs_p.html?deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=on" > /dev/null
./protectedPostApiCall.sh "IndexControlURLs_p.html" "deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=on"
@@ -1,3 +1,3 @@
#!/usr/bin/env sh
cd "`dirname $0`"
./apicall.sh "/IndexControlURLs_p.html?deleteIndex=off&deleteSolr=off&deleteCache=on&deleteCrawlQueues=off&deleteRobots=on&deleteSearchFl=on&deletecomplete=" > /dev/null
./protectedPostApiCall.sh "IndexControlURLs_p.html" "deleteIndex=off&deleteSolr=off&deleteCache=on&deleteCrawlQueues=off&deleteRobots=on&deleteSearchFl=on&deletecomplete="
@@ -1,3 +1,3 @@
#!/usr/bin/env sh
cd "`dirname $0`"
./apicall.sh "/IndexControlURLs_p.html?deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=off" > /dev/null
./protectedPostApiCall.sh "IndexControlURLs_p.html" "deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=off"
@@ -1,3 +1,3 @@
#!/usr/bin/env sh
cd "`dirname $0`"
./apicall.sh "/IndexControlURLs_p.html?urlhashdeleteall=&urlstring=$1" > /dev/null
./protectedPostApiCall.sh "IndexControlURLs_p.html" "urlhashdeleteall=&urlstring=$1"
@@ -6,7 +6,7 @@ if [ -z "$1" ]; then
exit 2
fi
(./apicall.sh "ConfigAccounts_p.html?setAdmin=&adminuser=admin&adminpw1=$1&adminpw2=$1&access=" > /dev/null && \
(./protectedPostApiCall.sh "ConfigAccounts_p.html" "setAdmin=&adminuser=admin&adminpw1=$1&adminpw2=$1&access=" && \
echo "Password for User Name 'admin' set to '$1'") || \
(echo "Password setting failed" && \
exit 1)
@@ -0,0 +1,77 @@
#!/usr/bin/env sh
# Call a YaCy HTTP POST API URL protected by HTTP authentication and transaction token validation
# $1 : API path
# $2 : POST parameters (example : "param1=value1&param2=value2")
#
# Authentication options :
# - enable unauthenticated local access as administrator : set adminAccountForLocalhost=true in the DATA/SETTINGS/yacy.conf file
# - OR use the legacy Basic HTTP authentication mode (unsecured for remote access): set the "auth-method" to BASIC in the defaults/web.xml file
# - OR use the Digest HTTP authentication mode : set the "auth-method" to DIGEST in the defaults/web.xml file.
# With that last option, the script will run in interactive mode as default, prompting for the administrator password.
# To run in batch mode, you must first export an environment variable filled with the clear-text administrator password before using this script :
# For example with > export YACY_ADMIN_PASSWORD=your_admin_password
#
cd "`dirname $0`"
port=$(grep ^port= ../DATA/SETTINGS/yacy.conf |cut -d= -f2)
admin=$(grep ^adminAccountUserName= ../DATA/SETTINGS/yacy.conf |cut -d= -f2)
adminAccountForLocalhost=$(grep ^adminAccountForLocalhost= ../DATA/SETTINGS/yacy.conf | cut -d= -f2)
if grep "<auth-method>BASIC</auth-method>" ../defaults/web.xml > /dev/null; then
# When authentication method is in basic mode, use directly the password hash from the configuration file
YACY_ADMIN_PASSWORD=$(grep ^adminAccountBase64MD5= ../DATA/SETTINGS/yacy.conf |cut -d= -f2)
fi
if which curl > /dev/null; then
if [ "$adminAccountForLocalhost" = "true" ]; then
# localhost access as administrator without authentication is enabled
# retrieve the transaction token from the HTTP GET flavor of the URL
transactionToken=$(curl -sSfI "http://127.0.0.1:$port/$1" | grep X-YaCy-Transaction-Token: | awk {'printf $2'} | tr -d '[:space:]')
# send POST data including the transaction token
curl -sSf -d "$2&transactionToken=$transactionToken" "http://127.0.0.1:$port/$1" > /dev/null
else
if [ -z "$YACY_ADMIN_PASSWORD" ]; then
# no password environment variable : we ask interactively for it only once (not using read -s to be POSIX compliant)
stty -echo
read -p "Enter host password for user '$admin':" YACY_ADMIN_PASSWORD
stty echo
printf "\n"
fi
# retrieve the transaction token from the HTTP GET flavor of the URL
transactionToken=$(curl -sSfI --anyauth -u "$admin:$YACY_ADMIN_PASSWORD" "http://127.0.0.1:$port/$1" | grep X-YaCy-Transaction-Token: | awk {'printf $2'} | tr -d '[:space:]')
# send POST data including the transaction token
curl -sSf --anyauth -u "$admin:$YACY_ADMIN_PASSWORD" -d "$2&transactionToken=$transactionToken" "http://127.0.0.1:$port/$1" > /dev/null
fi
elif which wget > /dev/null; then
if [ "$adminAccountForLocalhost" = "true" ]; then
# localhost access as administrator without authentication is enabled
# retrieve the transaction token from the HTTP GET flavor of the URL
transactionToken=$(wget -q -t 1 -O - --save-headers --timeout=120 "http://127.0.0.1:$port/$1" | grep X-YaCy-Transaction-Token: | awk {'printf $2'} | tr -d '[:space:]')
# send POST data including the transaction token
wget -nv -t 1 -O /dev/null --timeout=120 --post-data "$2&transactionToken=$transactionToken" "http://127.0.0.1:$port/$1"
else
if [ -z "$YACY_ADMIN_PASSWORD" ]; then
# no password environment variable : we ask interactively for it only once (not using read -s to be POSIX compliant)
stty -echo
read -p "Enter host password for user '$admin':" YACY_ADMIN_PASSWORD
stty echo
printf "\n"
fi
# retrieve the transaction token from the HTTP GET flavor of the URL
transactionToken=$(wget -q -t 1 -O - --http-user "$admin" --http-password "$YACY_ADMIN_PASSWORD" --save-headers --timeout=120 "http://127.0.0.1:$port/$1" | grep X-YaCy-Transaction-Token: | awk {'printf $2'} | tr -d '[:space:]')
# send POST data including the transaction token
wget -nv -t 1 -O /dev/null --timeout=120 --http-user "$admin" --http-password "$YACY_ADMIN_PASSWORD" --post-data "$2&transactionToken=$transactionToken" "http://127.0.0.1:$port/$1"
fi
else
printf "Please install curl or wget\n" > /dev/stderr
exit 1
fi
@@ -30,6 +30,7 @@ <h2>User Administration</h2>
<fieldset><legend>Admin Account</legend>
<form action="ConfigAccounts_p.html" method="post" accept-charset="UTF-8">
<input type="hidden" name="transactionToken" value="#[transactionToken]#"/>
<fieldset>
<legend>
<input type="radio" name="access" id="access_localhost" value="localhost"#(localhost.checked)#:: checked="checked"#(/localhost.checked)# />
@@ -60,6 +61,7 @@ <h2>User Administration</h2>
<fieldset><legend>Access Rules</legend>
<form action="ConfigAccounts_p.html" method="post" accept-charset="UTF-8">
<input type="hidden" name="transactionToken" value="#[transactionToken]#"/>
<dl class="userConfig">
<dt>Protection of all pages: if set to on, access to all pages need authorization; if off, only pages with "_p" extension are protected.</dt>
<dd><input type="checkbox" name="adminAccountAllPages" data-size="small"#(adminAccountAllPages.checked)#:: checked="checked"#(/adminAccountAllPages.checked)#></dd>
@@ -74,6 +76,7 @@ <h2>User Administration</h2>
<fieldset><legend>User Accounts</legend>
<form action="ConfigAccounts_p.html" accept-charset="UTF-8">
<input type="hidden" name="transactionToken" value="#[transactionToken]#"/>
<fieldset><legend>Select user</legend>
<dl>
<dt><label for="user">Select user</label>:</dt>
@@ -93,6 +96,7 @@ <h2>User Administration</h2>
</form>
<form action="ConfigAccounts_p.html" method="post" accept-charset="UTF-8">
<input type="hidden" name="transactionToken" value="#[transactionToken]#"/>
<fieldset><legend>Edit current user: #[username]#</legend>
<!-- Hidden(text for debugging): <input type="text" name="current_user" value="#[current_user]#" readonly> -->
<input type="hidden" name="current_user" value="#[current_user]#" />
@@ -33,9 +33,11 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.yacy.cora.order.Digest;
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.cora.util.ConcurrentLog;
import net.yacy.data.TransactionManager;
import net.yacy.data.UserDB;
import net.yacy.data.UserDB.AccessRight;
import net.yacy.http.Jetty9HttpServerImpl;
@@ -45,21 +47,27 @@
import net.yacy.server.serverSwitch;
public class ConfigAccounts_p {
public static serverObjects respond(@SuppressWarnings("unused") final RequestHeader header, final serverObjects post, final serverSwitch env) {
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
final serverObjects prop = new serverObjects();
/* Acquire a transaction token for the next POST form submission */
prop.put(TransactionManager.TRANSACTION_TOKEN_PARAM, TransactionManager.getTransactionToken(header));
final Switchboard sb = Switchboard.getSwitchboard();
UserDB.Entry entry = null;
// admin password
boolean localhostAccess = sb.getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false);
if (post != null && post.containsKey("setAccess")) {
TransactionManager.checkPostTransaction(header, post);
sb.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_All_PAGES, post.getBoolean(SwitchboardConstants.ADMIN_ACCOUNT_All_PAGES));
}
if (post != null && post.containsKey("setAdmin")) {
TransactionManager.checkPostTransaction(header, post);
localhostAccess = post.get("access", "").equals("localhost");
final String user = post.get("adminuser", "");
final String pw1 = post.get("adminpw1", "");
@@ -149,6 +157,7 @@ public static serverObjects respond(@SuppressWarnings("unused") final RequestHea
//user=from userlist
//current_user = edited user
} else if (post.containsKey("user") && !"newuser".equals(post.get("user"))){
TransactionManager.checkPostTransaction(header, post);
if (post.containsKey("change_user")) {
//defaults for newuser are set above
entry = sb.userDB.getEntry(post.get("user"));
@@ -174,6 +183,7 @@ public static serverObjects respond(@SuppressWarnings("unused") final RequestHea
sb.userDB.removeEntry(post.get("user"));
}
} else if (post.containsKey("change")) { //New User / edit User
TransactionManager.checkPostTransaction(header, post);
prop.put("text", "0");
prop.put("error", "0");
@@ -55,6 +55,7 @@ <h2>Advanced Config</h2>
#{/options}#
</select><br />
<form action="ConfigProperties_p.html" method="post" accept-charset="UTF-8">
<input type="hidden" name="transactionToken" value="#[transactionToken]#" />
<fieldset>
<input type="text" id="key" name="key" size="40" onkeyup="filterList()" value="#[keyPosted]#"/>:<input type="text" id="value" name="value" size="40" value="#[valuePosted]#" />
<input type="submit" value="Save" class="btn btn-primary"/>&nbsp;
@@ -34,20 +34,27 @@
import java.util.List;
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.data.TransactionManager;
import net.yacy.server.serverObjects;
import net.yacy.server.serverSwitch;
public class ConfigProperties_p {
public static serverObjects respond(@SuppressWarnings("unused") final RequestHeader header, final serverObjects post, final serverSwitch env) {
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
// return variable that accumulates replacements
final serverObjects prop = new serverObjects();
/* Acquire a transaction token for the next POST form submission */
prop.put(TransactionManager.TRANSACTION_TOKEN_PARAM, TransactionManager.getTransactionToken(header));
String key = "";
String value = "";
//change a key
if (post != null && post.containsKey("key") && post.containsKey("value")) {
/* Check the transaction is valid */
TransactionManager.checkPostTransaction(header, post);
key = post.get("key").trim();
value = post.get("value").trim();
if (key != null && !key.isEmpty()) {
@@ -1,7 +1,6 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!DOCTYPE html>
<html>
<head>
#(forwardToSteering)#::<meta http-equiv="REFRESH" content="0; url=/Steering.html?update=1&amp;releaseinstall=#[release]#" />#(/forwardToSteering)#
<title>YaCy '#[clientname]#': System Update</title>
#%env/templates/metas.template%#
</head>
@@ -35,19 +34,21 @@ <h2>System Update</h2>
#(/downloadError)#
</p></form></dd>
<dt><br />Downloaded Releases</dt>
<dd><form action="ConfigUpdate_p.html" method="get" accept-charset="UTF-8"><p>
<dd><form action="Steering.html" method="post" accept-charset="UTF-8"><p>
#(downloadsAvailable)#
No downloaded releases available for deployment.
::
<input type="hidden" name="update" value="1"/>
&nbsp;<select name="releaseinstall">
#(/downloadsAvailable)#
#{downloadedreleases}#
<option #(selected)#::selected="selected"#(/selected)# value="#[file]#">#[name]# #(signature)#(no signature)::(signed)#(/signature)#</option>
#{/downloadedreleases}#
#(downloadsAvailable)#::</select>#(/downloadsAvailable)#
#(deployenabled)#::no&nbsp;automated installation on development environments::
<input type="hidden" name="transactionToken" value="#[transactionToken]#"/>
&nbsp;&nbsp;<input type="submit" name="update" class="btn btn-primary" value="Install Release" #(buttonsActive)#disabled="disabled"::#(/buttonsActive)#/>
&nbsp;&nbsp;<input type="submit" name="deleteRelease" class="btn btn-danger" value="Delete Release" #(buttonsActive)#disabled="disabled"::#(/buttonsActive)#/>
&nbsp;&nbsp;<input type="submit" name="deleteRelease" formaction="ConfigUpdate_p.html" class="btn btn-danger" value="Delete Release" #(buttonsActive)#disabled="disabled"::#(/buttonsActive)#/>
#(/deployenabled)#
</p></form></dd>
<dt><br />Automatic Update</dt>
@@ -35,6 +35,7 @@
import net.yacy.cora.document.id.DigestURL;
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.cora.util.ConcurrentLog;
import net.yacy.data.TransactionManager;
import net.yacy.kelondro.util.FileUtils;
import net.yacy.kelondro.util.OS;
import net.yacy.peers.operation.yacyBuildProperties;
@@ -46,7 +47,7 @@
public class ConfigUpdate_p {
public static serverObjects respond(@SuppressWarnings("unused") final RequestHeader header, final serverObjects post, final serverSwitch env) {
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
// return variable that accumulates replacements
final serverObjects prop = new serverObjects();
final Switchboard sb = (Switchboard) env;
@@ -73,15 +74,6 @@ public static serverObjects respond(@SuppressWarnings("unused") final RequestHea
prop.put("candeploy_downloadError", "0");
if (post != null) {
// check if update is supposed to be installed and a release is defined
if (post.containsKey("update") && !post.get("releaseinstall", "").isEmpty()) {
prop.put("forwardToSteering", "1");
prop.putHTML("forwardToSteering_release",post.get("releaseinstall", ""));
prop.put("deploys", "1");
prop.put("candeploy", "2"); // display nothing else
return prop;
}
if (post.containsKey("downloadRelease")) {
// download a release
final String release = post.get("releasedownload", "");
@@ -208,6 +200,8 @@ public static serverObjects respond(@SuppressWarnings("unused") final RequestHea
// check if there are any downloaded releases and if there are enable the update buttons
prop.put("candeploy_downloadsAvailable", (downloadedReleases.isEmpty()) ? "0" : "1");
prop.put("candeploy_deployenabled_buttonsActive", (downloadedReleases.isEmpty() || devenvironment) ? "0" : "1");
/* Acquire a transaction token for the update operation */
prop.put("candeploy_deployenabled_" + TransactionManager.TRANSACTION_TOKEN_PARAM, TransactionManager.getTransactionToken(header, "/Steering.html"));
int relcount = 0;
for(final yacyRelease release : downloadedReleases) {
@@ -28,6 +28,7 @@ <h2>Reverse Word Index Administration</h2>
#(limitations)#::
<form action="IndexControlRWIs_p.html" method="post" enctype="multipart/form-data" accept-charset="UTF-8">
<input type="hidden" name="transactionToken" value="#[transactionToken]#" />
<fieldset><legend>Limitations</legend>
<dl>
<dt class="TableCellDark">Index Reference Size</dt>
@@ -47,6 +48,7 @@ <h2>Reverse Word Index Administration</h2>
<p>No entry for word hash #[wordhash]#</p>::
<p>Search result:
<form name="selection" action="IndexControlRWIs_p.html" method="post" enctype="multipart/form-data">
<input type="hidden" name="transactionToken" value="#[transactionToken]#" />
<table border="0">
<tr class="TableHeader">
<td style="background-color:#FFFFFF">&nbsp;</td>
Oops, something went wrong.

0 comments on commit cde237b

Please sign in to comment.