dify icon indicating copy to clipboard operation
dify copied to clipboard

Unauthorized SSRF vulnerability in controllers.console.remote_files.RemoteFileUploadApi

Open UzJu opened this issue 10 months ago • 3 comments

Self Checks

  • [x] This is only for bug report, if you would like to ask a question, please head to Discussions.
  • [x] I have searched for existing issues search for existing issues, including closed ones.
  • [x] I confirm that I am using English to submit this report (我已阅读并同意 Language Policy).
  • [x] [FOR CHINESE USERS] 请务必使用英文提交 Issue,否则会被关闭。谢谢!:)
  • [x] Please do not modify this template :) and fill in all the required fields.

Dify version

1.0.0

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

The class controllers.console.remote_files.RemoteFileUploadApi will get the URL parameter to initiate the request externally through ssrf_proxy.head, but actually ssrf_proxy.head calls core.helper.ssrf_ proxy.make_request, will also unconditionally outbound request, theoretically if this is a business function, please do the authentication process for the interface, do not let unauthorized users access the

  • controllers.console.remote_files.RemoteFileUploadApi

Image

Image

POST /console/api/remote-files/upload HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Priority: u=0, i
Content-Type: application/json

{"url":"http://192.168.1.94:1234"}

At this point my terminal received the request normally

Image

Image Restoration Recommendations If this is a normal function that requires access to a remote address, then it is recommended to authenticate the interface

✔️ Expected Behavior

If this is a normal function that requires access to a remote address, then it is recommended to authenticate the interface

❌ Actual Behavior

No response

UzJu avatar Mar 07 '25 03:03 UzJu

Hey @UzJu! I'm here to help you with any bugs, questions, or contributions you have for the project. Let me know how I can assist you!

To address the unauthorized SSRF vulnerability in the RemoteFileUploadApi class, you should implement proper authentication to ensure that only authorized users can access remote URLs. Here's a solution you can consider:

  1. Add Authentication: Use the @login_required decorator to ensure that the user is authenticated before accessing the endpoint.

  2. Authorization Check: Implement a function to verify if the user is authorized to access the specified URL. This function should contain your business logic for authorization.

Here's an example implementation:

from flask_login import login_required

class RemoteFileUploadApi(WebApiResource):
    @marshal_with(file_fields_with_signed_url)
    @login_required
    def post(self, app_model, end_user):
        parser = reqparse.RequestParser()
        parser.add_argument("url", type=str, required=True, help="URL is required")
        args = parser.parse_args()

        url = args["url"]

        # Check if the user is authorized to access the URL
        if not is_user_authorized_to_access_url(end_user, url):
            raise RemoteFileUploadError("User is not authorized to access this URL.")

        try:
            resp = ssrf_proxy.head(url=url)
            if resp.status_code != httpx.codes.OK:
                resp = ssrf_proxy.get(url=url, timeout=3, follow_redirects=True)
            if resp.status_code != httpx.codes.OK:
                raise RemoteFileUploadError(f"Failed to fetch file from {url}: {resp.text}")
        except httpx.RequestError as e:
            raise RemoteFileUploadError(f"Failed to fetch file from {url}: {str(e)}")

        file_info = helpers.guess_file_info_from_response(resp)

        if not FileService.is_file_size_within_limit(extension=file_info.extension, file_size=file_info.size):
            raise FileTooLargeError

        content = resp.content if resp.request.method == "GET" else ssrf_proxy.get(url).content

        try:
            upload_file = FileService.upload_file(
                filename=file_info.filename,
                content=content,
                mimetype=file_info.mimetype,
                user=end_user,
                source_url=url,
            )
        except services.errors.file.FileTooLargeError as file_too_large_error:
            raise FileTooLargeError(file_too_large_error.description)
        except services.errors.file.UnsupportedFileTypeError:
            raise UnsupportedFileTypeError
        
        return {
            "id": upload_file.id,
            "name": upload_file.name,
            "size": upload_file.size,
            "extension": upload_file.extension,
            "url": file_helpers.get_signed_file_url(upload_file_id=upload_file.id),
            "mime_type": upload_file.mime_type,
            "created_by": upload_file.created_by,
            "created_at": upload_file.created_at,
        }, 201

def is_user_authorized_to_access_url(user, url):
    # Implement your logic to check if the user is authorized to access the URL
    return True  # Placeholder implementation

This code ensures that the user is logged in and authorized to access the URL before proceeding with the file upload process [1][2].

To continue talking to Dosu, mention @dosu.


Help Dosu learn! Give it feedback: Great Response | Irrelevant Answer | Incorrect Sources | Too Verbose | Hallucination | Bug Report | Other

dosubot[bot] avatar Mar 07 '25 03:03 dosubot[bot]

I noticed that Squid's configuration doesn't limit the IP addresses that can be accessed, is this some kind of risk? After all, the interface doesn't need to be authenticated Image

UzJu avatar Mar 07 '25 04:03 UzJu

Personally, I think that if there is a need for an external request, then there must be a permission check on the interface, so that people who do not need to log in, can't call the interface directly.

UzJu avatar Mar 07 '25 04:03 UzJu

Hi, @UzJu. I'm Dosu, and I'm helping the Dify team manage their backlog. I'm marking this issue as stale.

Issue Summary:

  • An unauthorized SSRF vulnerability was reported in the RemoteFileUploadApi class.
  • You suggested implementing authentication to prevent exploitation.
  • There is a potential risk with Squid's configuration not limiting accessible IP addresses.
  • Emphasized the importance of permission checks for external requests.

Next Steps:

  • Please let us know if this issue is still relevant to the latest version of the Dify repository by commenting here.
  • If there is no further activity, this issue will be automatically closed in 15 days.

Thank you for your understanding and contribution!

dosubot[bot] avatar Apr 07 '25 16:04 dosubot[bot]

hi, are there any fixes or workaround yet?

phucdc avatar Apr 15 '25 03:04 phucdc

Is this security issue resolved now?

berg-wang avatar Jun 24 '25 03:06 berg-wang

See https://github.com/langgenius/dify/pull/24393#issue-3348714677

crazywoola avatar Aug 29 '25 06:08 crazywoola