Suppose I have a function that redirects the page to the given URL (code below). For security reasons, I want to modify it so that it redirects only to the URLs inside my domain.
public static function redirect($num, $url)
{
header($http[$num]);
header ("Location: $url");
}
This is based more like TimWolla's example:
public static function redirect( $num, $url ) {
$host = ( parse_url( $url, PHP_URL_HOST );
if ( !empty( $host ) && $host == $_SERVER[ 'HTTP_HOST' ] ) {
$url = preg_replace( '/[^\w\s\p{L}\d\r?,=#:\/.-]/i', '', urldecode( $url ) );
#header( $http[ $num ] ); //not sure what this is for?
header( "Location: " . $url );
return;
} else return
}
All I have changed is rather than posting an error, the function just returns. Also added a filter to remove characters using a whitelist method.
The concept above is principle to build upon I think.
Use parse_url():
public static function redirect($num, $url)
{
if (parse_url($url, PHP_URL_HOST) != 'example.com') {
trigger_error("Invalid redirection-URL given", E_USER_ERROR);
return;
}
header($http[$num]);
header ("Location: $url");
}
Take a look at http://php.net/manual/en/reserved.variables.server.php.
You can find the host name there and then just do a strpos to ensure that the $url matches
you can find out your current host using $_SERVER['HTTP_HOST'] so you can check if the url contains the host before deciding what to do with it
$pos = strpos($url, $_SERVER['HTTP_HOST']);
if ($pos === false) {
//not on same domain don't redirect
} else {
//on same domain so do redirection
}
I would keep it as simple as possible, using strpos:
if ( strpos( $url, 'yourdomain.com' ) === false )
header('Location: /an/error/page.html');
else
header('Location: '.$url);
Related
I've just finished building my first plugin and have tested it with various plugins on my personal site with no errors. However some users are saying the plugin is causing the following errors for them:
strpos(): Empty needle in
/west/XXXXX/public_html/wp-content/plugins/bot-block/bot-plugin.php on
line 200
On line 200 I have this:
//See if the domain that referred is in the current block url
$pos = strpos( $referrer, $site );
Now I can't see a problem with that line so I'll give you the whole function:
//Check referrer function
function bot_block_parse()
{
//Get the options for the plugin
$options = get_option( 'bot_block' );
//See if the request was from another site
if( isset( $_SERVER['HTTP_REFERER'] ) )
{
//Split the URL into it's components
$referrer = parse_url( $_SERVER['HTTP_REFERER'] );
//Trim the components
$referrer = array_map( 'trim', $referrer );
//Get the domain name
$referrer = $referrer['host'];
//Get the block list
$list = $this->create_block_list();
//Loop through all the blocked domains
foreach( $list as $site )
{
//Trim the domain
$site = trim( $site );
//Set the prefix for domains that aren't sub domains
$prefix = 'www';
//Split domain into smaller components
$domainParts = explode( ".", $referrer );
//See if the domain that referred is in the current block url
$pos = strpos( $referrer, $site );
//See if block subdomains is checked
if( isset( $options['subdomains'] ) )
{
//Check to see if the domain was the current blocked site and if the prefix is not www
if( $pos !== false && $domainParts[0] != $prefix )
{
//Log spam
$this->log_spam( $site );
//Call the redirect function to see where to send the user
$this->bot_block_redirect();
exit;
}
}
//See if the domain was the current site blocked and the prefix is www
if( $pos !== false && $domainParts[0] == $prefix )
{
//Log spam
$this->log_spam( $site );
//Call the redirect function to see where to send the user
$this->bot_block_redirect();
exit;
}
}
}
}
If you need to see the full plugin code I have put it on pastebin here: http://pastebin.com/gw7YbPVa
Can anybody help me figure this out please?
The quick fix is to see if your needle ($site) is empty before attempting to call strpos(). If it is empty, certainly it can't be found in the haystack, so we should skip altogether and set $pos to false.
$pos = strpos( $referrer, $site );
Becomes:
if ( $site == '' || !$site ) {
$pos = false;
} else {
$pos = strpos( $referrer, $site );
}
The better solution is to determine why your $site variable is empty in the first place. Does each child element in $list array contain another array, instead of a string as you expect? You can use var_dump( $site ); in your loop to see the contents of that variable.
i have a question about these function: $_SERVER["REQUEST_URI"]. Can somebody tell me if it's safe to use like i use it ( these form i use for new topic in forum )?
<form name="vpid" action="<?php echo htmlspecialchars($_SERVER["REQUEST_URI"]);?>" method="post">
Thank you
The first thing I'd say is that you probably don't need REQUEST_URI in this in this context.
If you want a form to post back to the current page, the action attribute can be set to blank string or a dot; you don't need to specify the whole current URL.
In cases where you do need it, the answer is that yes, REQUEST_URI is safe.
A lot of values in $_SERVER are not safe, so it's good to be cautious, but REQUEST_URI is safe because it represents the address that was used to get to the site; if the address is invalid, then the user wouldn't have been able to get to the server in the first place.
Other $_SERVER fields can be hacked; it's trivial to spoof things like REMOTE_HOST and HTTP_REFERER, so you should never rely on them to be reliable, but REQUEST_URI ought to be safe.
The main thing here though is that you shouldn't really need it anyway.
Already an old question, but no, you cannot trust $_SERVER['REQUEST_URI'] because, it will only be available on an apache server.
Here is how Drupal handles it on the 7.x version
function request_uri() {
if (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
}
else {
if (isset($_SERVER['argv'])) {
$uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
}
elseif (isset($_SERVER['QUERY_STRING'])) {
$uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
}
else {
$uri = $_SERVER['SCRIPT_NAME'];
}
}
// Prevent multiple slashes to avoid cross site requests via the Form API.
$uri = '/' . ltrim($uri, '/');
return $uri;
}
And the WordPress version from v3.0.0 up to now. Its purpose is to fill in the blanks and normalize the $_SERVER variables.
function wp_fix_server_vars() {
global $PHP_SELF;
$default_server_values = array(
'SERVER_SOFTWARE' => '',
'REQUEST_URI' => '',
);
$_SERVER = array_merge( $default_server_values, $_SERVER );
// Fix for IIS when running with PHP ISAPI.
if ( empty( $_SERVER['REQUEST_URI'] ) || ( 'cgi-fcgi' !== PHP_SAPI && preg_match( '/^Microsoft-IIS\//', $_SERVER['SERVER_SOFTWARE'] ) ) ) {
if ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {
// IIS Mod-Rewrite.
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL'];
} elseif ( isset( $_SERVER['HTTP_X_REWRITE_URL'] ) ) {
// IIS Isapi_Rewrite.
$_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
} else {
// Use ORIG_PATH_INFO if there is no PATH_INFO.
if ( ! isset( $_SERVER['PATH_INFO'] ) && isset( $_SERVER['ORIG_PATH_INFO'] ) ) {
$_SERVER['PATH_INFO'] = $_SERVER['ORIG_PATH_INFO'];
}
// Some IIS + PHP configurations put the script-name in the path-info (no need to append it twice).
if ( isset( $_SERVER['PATH_INFO'] ) ) {
if ( $_SERVER['PATH_INFO'] == $_SERVER['SCRIPT_NAME'] ) {
$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
} else {
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
}
}
// Append the query string if it exists and isn't null.
if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
}
}
}
// Fix for PHP as CGI hosts that set SCRIPT_FILENAME to something ending in php.cgi for all requests.
if ( isset( $_SERVER['SCRIPT_FILENAME'] ) && ( strpos( $_SERVER['SCRIPT_FILENAME'], 'php.cgi' ) == strlen( $_SERVER['SCRIPT_FILENAME'] ) - 7 ) ) {
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['PATH_TRANSLATED'];
}
// Fix for Dreamhost and other PHP as CGI hosts.
if ( strpos( $_SERVER['SCRIPT_NAME'], 'php.cgi' ) !== false ) {
unset( $_SERVER['PATH_INFO'] );
}
// Fix empty PHP_SELF.
$PHP_SELF = $_SERVER['PHP_SELF'];
if ( empty( $PHP_SELF ) ) {
$_SERVER['PHP_SELF'] = preg_replace( '/(\?.*)?$/', '', $_SERVER['REQUEST_URI'] );
$PHP_SELF = $_SERVER['PHP_SELF'];
}
}
Symfony HttpFoundation method is a little bit more complex.
I'm getting all ahrefs of a page with this loop:
foreach($html->find('a[href!="#"]') as $ahref) {
$ahrefs++;
}
I want to do something like this:
foreach($html->find('a[href!="#"]') as $ahref) {
if(isexternal($ahref)) {
$external++;
}
$ahrefs++;
}
Where isexternal is a function
function isexternal($url) {
// FOO...
// Test if link is internal/external
if(/*condition is true*/) {
return true;
}
else {
return false;
}
}
Help!
Use parse_url and compare host to your local host (often but not always it's the same as $_SERVER['HTTP_HOST'])
function isexternal($url) {
$components = parse_url($url);
return !empty($components['host']) && strcasecmp($components['host'], 'example.com'); // empty host will indicate url like '/relative.php'
}
Hovewer this will treat www.example.com and example.com as different hosts. If you want all your subdomains to be treated as local links then the function will be somewhat larger:
function isexternal($url) {
$components = parse_url($url);
if ( empty($components['host']) ) return false; // we will treat url like '/relative.php' as relative
if ( strcasecmp($components['host'], 'example.com') === 0 ) return false; // url host looks exactly like the local host
return strrpos(strtolower($components['host']), '.example.com') !== strlen($components['host']) - strlen('.example.com'); // check if the url host is a subdomain
}
This is how you can simply detect external URLs:
$url = 'https://my-domain.com/demo/';
$domain = 'my-domain.com';
$internal = (
false !== stripos( $url, '//' . $domain ) || // include "//my-domain.com" and "http://my-domain.com"
stripos( $url, '.' . $domain ) || // include subdomains, like "www.my-domain.com". DANGEROUS (see below)!
(
0 !== strpos( $url, '//' ) && // exclude protocol relative URLs, like "//example.com"
0 === strpos( $url, '/' ) // include root-relative URLs, like "/demo"
)
);
The above check will treat www.my-domain.com and my-domain.com as being "internal".
Why this rule is dangerous:
The subdomain logic introduces a weakness that could be exploited: When an external URL contains your domain inside the path, for example, https://external.com/www.my-domain.com is treated as internal!
More secure code:
This problem can be eliminated by removing subdomain support (which I suggest to do):
$url = 'https://my-domain.com/demo/';
$domain = 'my-domain.com';
$internal = (
false !== stripos( $url, '//' . $domain ) || // include "//my-domain.com" and "http://my-domain.com"
(
0 !== strpos( $url, '//' ) && // exclude protocol relative URLs, like "//example.com"
0 === strpos( $url, '/' ) // include root-relative URLs, like "/demo"
)
);
function isexternal($url) {
// FOO...
// Test if link is internal/external
if(strpos($url,'domainname.com') !== false || strpos($url,"/") === '0')
{
return true;
}
else
{
return false;
}
}
I know this post is old but here my function i coded right now. Maybe some other need it too.
function IsResourceLocal($url){
if( empty( $url ) ){ return false; }
$urlParsed = parse_url( $url );
$host = $urlParsed['host'];
if( empty( $host ) ){
/* maybe we have a relative link like: /wp-content/uploads/image.jpg */
/* add absolute path to begin and check if file exists */
$doc_root = $_SERVER['DOCUMENT_ROOT'];
$maybefile = $doc_root.$url;
/* Check if file exists */
$fileexists = file_exists ( $maybefile );
if( $fileexists ){
/* maybe you want to convert to full url? */
return true;
}
}
/* strip www. if exists */
$host = str_replace('www.','',$host);
$thishost = $_SERVER['HTTP_HOST'];
/* strip www. if exists */
$thishost = str_replace('www.','',$thishost);
if( $host == $thishost ){
return true;
}
return false;
}
You probably want to check if the link is in the same domain. That will only work though if all your href attributes are absolute and contain the domain. Relative ones like /test/file.html are tricky because one can have folders that have the same name as domains.. So, if you have full url's in each link:
function isexternal($url) {
// Test if link is internal/external
if(stristr($url, "myDomain.com") || strpos($url,"/") == '0')
return true;
else
return false;
}
Today I tried to update Wordpress to the latest version (3.5.1). After doing this, I can't open wp-admin/index.php anymore. It gives me a 404 error. I've looked into the index.php file and it breaks when the function auth_redirect() is called. Here's the code of that function:
function auth_redirect() {
// Checks if a user is logged in, if not redirects them to the login page
$secure = ( is_ssl() || force_ssl_admin() );
$secure = apply_filters('secure_auth_redirect', $secure);
// If https is required and request is http, redirect
if ( $secure && !is_ssl() && false !== strpos($_SERVER['REQUEST_URI'], 'wp-admin') ) {
if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) {
wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
exit();
} else {
wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
exit();
}
}
if ( is_user_admin() )
$scheme = 'logged_in';
else
$scheme = apply_filters( 'auth_redirect_scheme', '' );
if ( $user_id = wp_validate_auth_cookie( '', $scheme) ) {
do_action('auth_redirect', $user_id);
// If the user wants ssl but the session is not ssl, redirect.
if ( !$secure && get_user_option('use_ssl', $user_id) && false !== strpos($_SERVER['REQUEST_URI'], 'wp-admin') ) {
if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) {
wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
exit();
} else {
wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
exit();
}
}
return; // The cookie is good so we're done
}
// The cookie is no good so force login
nocache_headers();
$redirect = ( strpos( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) ? wp_get_referer() : set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
$login_url = wp_login_url($redirect, true);
wp_redirect($login_url);
exit();
}
However I can't find the specific part where it breaks, since it's not giving me an error message, it just shows a 404 page, and in Firefox it says it's not redirecting correctly.
Could someone please help me out on this?
Thank you!
Some additional information:
I've found the line where it breaks, it is:
wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
Echoing $_SERVER['HTTP_HOST'] and $_SERVER['REQUEST_URI'] gives me the expected result (www.domain.com/blog). However it just doesn't work :(
I have had similar issues in the past. But it usually involves a new plugin installation... Couple of things:
Try to increase the RAM available. WP has given me hassles in the past similar to this and it was RAM related. You should be able to increase the RAM in your .htaccess file
Is this a production system? If not, perhaps make a note of all the plugins, delete them, try access the system. If that works, then you know the issue was plugin based and not WP itself. You can then systematically add the plugins back one by one and determine which one caused the issue.
How can I add http:// to a URL if it doesn't already include a protocol (e.g. http://, https:// or ftp://)?
Example:
addhttp("google.com"); // http://google.com
addhttp("www.google.com"); // http://www.google.com
addhttp("google.com"); // http://google.com
addhttp("ftp://google.com"); // ftp://google.com
addhttp("https://google.com"); // https://google.com
addhttp("http://google.com"); // http://google.com
addhttp("rubbish"); // http://rubbish
A modified version of #nickf code:
function addhttp($url) {
if (!preg_match("~^(?:f|ht)tps?://~i", $url)) {
$url = "http://" . $url;
}
return $url;
}
Recognizes ftp://, ftps://, http:// and https:// in a case insensitive way.
At the time of writing, none of the answers used a built-in function for this:
function addScheme($url, $scheme = 'http://')
{
return parse_url($url, PHP_URL_SCHEME) === null ?
$scheme . $url : $url;
}
echo addScheme('google.com'); // "http://google.com"
echo addScheme('https://google.com'); // "https://google.com"
See also: parse_url()
Simply check if there is a protocol (delineated by "://") and add "http://" if there isn't.
if (false === strpos($url, '://')) {
$url = 'http://' . $url;
}
Note: This may be a simple and straightforward solution, but Jack's answer using parse_url is almost as simple and much more robust. You should probably use that one.
The best answer for this would be something like this:
function addhttp($url, $scheme="http://" )
{
return $url = empty(parse_url($url)['scheme']) ? $scheme . ltrim($url, '/') : $url;
}
The protocol flexible, so the same function can be used with ftp, https, etc.
Scan the string for ://. If it does not have it, prepend http:// to the string... Everything else just use the string as is.
This will work unless you have a rubbish input string.
Try this. It is not watertight1, but it might be good enough:
function addhttp($url) {
if (!preg_match("#^[hf]tt?ps?://#", $url)) {
$url = "http://" . $url;
}
return $url;
}
1. That is, prefixes like "fttps://" are treated as valid.
nickf's solution modified:
function addhttp($url) {
if (!preg_match("#^https?://#i", $url) && !preg_match("#^ftps?://#i", $url)) {
$url = "http://" . $url;
}
return $url;
}
<?php
if (!preg_match("/^(http|ftp):/", $_POST['url'])) {
$_POST['url'] = 'http://'.$_POST['url'];
}
$url = $_POST['url'];
?>
This code will add http:// to the URL if it’s not there.