对php中file_get_contents方法进行源码改造

AI 摘要

PHP file_get_contents函数安全增强,限制访问指定域名 - 实现php.ini中allow_domain参数配置,仅允许访问白名单域名,防止非法请求。

需求,在php.ini中设置 allow_domain 参数 格式如下 allow_domain = avc.com,xyz.cn 当php使用file_get_contents($url)时,需要判断url中主机名称是否被allow_domain所包含

在php安装包内找到file_get_contents的源文件,进行修改


PHP_FUNCTION(file_get_contents)
{
    char *filename;
    size_t filename_len;
    zend_bool use_include_path = 0;
    php_stream *stream;
    zend_long offset = 0;
    zend_long maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
    zval *zcontext = NULL;
    php_stream_context *context = NULL;
    zend_string *contents;

    /* Parse arguments */
    ZEND_PARSE_PARAMETERS_START(1, 5)
        Z_PARAM_PATH(filename, filename_len)
        Z_PARAM_OPTIONAL
        Z_PARAM_BOOL(use_include_path)
        Z_PARAM_RESOURCE_EX(zcontext, 1, 0)
        Z_PARAM_LONG(offset)
        Z_PARAM_LONG(maxlen)
    ZEND_PARSE_PARAMETERS_END();

    
    
    /*Check remote request is illegal . by netstat in 20200526 */
    
    
    char *allow_domain ;

    
    if (FAILURE != cfg_get_string("allow_domain", &allow_domain) && strlen(allow_domain) > 0 ) {
            char *url = (char *)malloc(strlen(filename) + 1);
            strcpy(url,filename);
            char http[5];
            char ftp[4];
            char *domains = "";
            int allowRequest = 0 ;
            char pattern[strlen(allow_domain)];
            
            memset(http,0,sizeof(http));
            memset(ftp,0,sizeof(ftp));
            
            strcpy(pattern,allow_domain);
            strncpy(http,url,sizeof(http) - 1);
            strncpy(ftp,url,sizeof(ftp) - 1);
            
            if(strcmp(http,"http") == 0 || strcmp(ftp,"ftp") == 0){
                 //获取url中的域名
                char *delim = "/";
                char *pToken = NULL;
                char *pSave = NULL;
                strtok_r(url, delim, &pSave);
                domains = strtok_r(NULL, delim,&pSave);
                // printf("%s\n",domains);

                //获取php.ini中allow_domain得设置
                delim = ",";
                pToken = NULL;
                pSave = NULL;
                pToken = strtok_r(pattern, delim, &pSave);
                int length , pos = 0;
                char dString[33];
                while(pToken){
                    length = strlen(pToken);
                    pos = strlen(domains) - length;
                    strncpy(dString,domains+pos,length);
                    // printf("pattern:%s,strlen(pattren):%d,url:%s\n",pToken,length,dString);
                    if(strcmp(pToken,dString) == 0){
                        allowRequest = 1;
                        break;
                    }
                    
                    pToken = strtok_r(NULL, delim, &pSave);
                    
                }
                
                
                    free(url);

                if(allowRequest == 0 ){
                    char *errmsg = "Illegal Url Request:";
                    char *result = malloc(strlen(errmsg) +strlen(filename)+ 1);
                    strcpy(result,errmsg);
                    strcat(result,filename);
                    // free(result);
                    php_error_docref(NULL, E_ERROR, result);
                    RETURN_FALSE;
                }
            }
                
    } 

    if (ZEND_NUM_ARGS() == 5 && maxlen < 0) {
        php_error_docref(NULL, E_WARNING, "length must be greater than or equal to zero");
        RETURN_FALSE;
    }

    context = php_stream_context_from_zval(zcontext, 0);

    stream = php_stream_open_wrapper_ex(filename, "rb",
                (use_include_path ? USE_PATH : 0) | REPORT_ERRORS,
                NULL, context);
    if (!stream) {
        RETURN_FALSE;
    }

    if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) {
        php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
        php_stream_close(stream);
        RETURN_FALSE;
    }

    if (maxlen > INT_MAX) {
        php_error_docref(NULL, E_WARNING, "maxlen truncated from " ZEND_LONG_FMT " to %d bytes", maxlen, INT_MAX);
        maxlen = INT_MAX;
    }
    if ((contents = php_stream_copy_to_mem(stream, maxlen, 0)) != NULL) {
        RETVAL_STR(contents);
    } else {
        RETVAL_EMPTY_STRING();
    }

    php_stream_close(stream);
}